Commit 68eb1f20 authored by Jens Segers's avatar Jens Segers

Merge master

parents a54ef6d9 5b7f335a
...@@ -16,6 +16,12 @@ before_script: ...@@ -16,6 +16,12 @@ before_script:
- echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- mysql -e 'create database unittest;' - mysql -e 'create database unittest;'
- composer self-update - composer self-update
- composer require satooshi/php-coveralls:dev-master
- composer install --dev --no-interaction - composer install --dev --no-interaction
script: phpunit script:
- mkdir -p build/logs
- phpunit --coverage-clover build/logs/clover.xml
after_script:
- php vendor/bin/coveralls -v
Laravel MongoDB Laravel MongoDB
=============== ===============
[![Latest Stable Version](https://poser.pugx.org/jenssegers/mongodb/v/stable.png)](https://packagist.org/packages/jenssegers/mongodb) [![Total Downloads](https://poser.pugx.org/jenssegers/mongodb/downloads.png)](https://packagist.org/packages/jenssegers/mongodb) [![Build Status](https://travis-ci.org/jenssegers/Laravel-MongoDB.png?branch=master)](https://travis-ci.org/jenssegers/Laravel-MongoDB) [![Latest Stable Version](https://poser.pugx.org/jenssegers/mongodb/v/stable.png)](https://packagist.org/packages/jenssegers/mongodb) [![Total Downloads](https://poser.pugx.org/jenssegers/mongodb/downloads.png)](https://packagist.org/packages/jenssegers/mongodb) [![Build Status](https://travis-ci.org/jenssegers/Laravel-MongoDB.png?branch=master)](https://travis-ci.org/jenssegers/Laravel-MongoDB) [![Coverage Status](https://coveralls.io/repos/jenssegers/Laravel-MongoDB/badge.png?branch=master)](https://coveralls.io/r/jenssegers/Laravel-MongoDB?branch=master)
An Eloquent model and Query builder with support for MongoDB, inspired by LMongo, but using the original Laravel methods. *This library extends the original Laravel classes, so it uses exactly the same methods.* An Eloquent model and Query builder with support for MongoDB, inspired by LMongo, but using the original Laravel methods. *This library extends the original Laravel classes, so it uses exactly the same methods.*
...@@ -44,7 +44,7 @@ You can connect to multiple servers or replica sets with the following configura ...@@ -44,7 +44,7 @@ You can connect to multiple servers or replica sets with the following configura
'mongodb' => array( 'mongodb' => array(
'driver' => 'mongodb', 'driver' => 'mongodb',
'host' => array('server1', 'server2), 'host' => array('server1', 'server2'),
'port' => 27017, 'port' => 27017,
'username' => 'username', 'username' => 'username',
'password' => 'password', 'password' => 'password',
...@@ -414,6 +414,11 @@ You can remove an embedded document by using the `destroy()` method: ...@@ -414,6 +414,11 @@ You can remove an embedded document by using the `destroy()` method:
// or // or
$user->books()->destroy($book); $user->books()->destroy($book);
If you want to add or remove embedded documents, without persistence, you can use the `associate` and `dissociate` methods. To write the changes to the database, save the parent object:
$user->books()->associate($book);
$user->save();
Again, you may override the conventional local key by passing a second argument to the embedsMany method: Again, you may override the conventional local key by passing a second argument to the embedsMany method:
return $this->embedsMany('Book', 'local_key'); return $this->embedsMany('Book', 'local_key');
...@@ -458,16 +463,23 @@ These expressions will be injected directly into the query. ...@@ -458,16 +463,23 @@ These expressions will be injected directly into the query.
User::whereRaw(array('age' => array('$gt' => 30, '$lt' => 40)))->get(); User::whereRaw(array('age' => array('$gt' => 30, '$lt' => 40)))->get();
You can also perform raw expressions on the internal MongoCollection object, note that this will return the original response, and not a collection of models. You can also perform raw expressions on the internal MongoCollection object. If this is executed on the model class, it will return a collection of models. If this is executed on the query builder, it will return the original response.
// Returns a collection of User models.
$models = User::raw(function($collection)
{
return $collection->find();
});
User::raw(function($collection) // Returns the original MongoCursor.
$cursor = DB::collection('users')->raw(function($collection)
{ {
return $collection->find(); return $collection->find();
}); });
Or you can access the internal MongoCollection object directly: Optional: if you don't pass a closure to the raw method, the internal MongoCollection object will be accessible:
User::raw()->find(); $model = User::raw()->findOne(array('age' => array('$lt' => 18)));
The MongoClient and MongoDB objects can be accessed like this: The MongoClient and MongoDB objects can be accessed like this:
......
...@@ -17,13 +17,13 @@ ...@@ -17,13 +17,13 @@
}, },
"require-dev": { "require-dev": {
"illuminate/cache": "4.2.x", "illuminate/cache": "4.2.x",
"illuminate/auth": "4.2.x" "illuminate/auth": "4.2.x",
"mockery/mockery": "*"
}, },
"autoload": { "autoload": {
"psr-0": { "psr-0": {
"Jenssegers\\Mongodb": "src/", "Jenssegers\\Mongodb": "src/",
"Jenssegers\\Eloquent": "src/" "Jenssegers\\Eloquent": "src/"
} }
}, }
"minimum-stability": "dev"
} }
...@@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany; ...@@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Eloquent\Relations\Relation;
use Jenssegers\Mongodb\Relations\BelongsTo; use Jenssegers\Mongodb\Relations\BelongsTo;
use Jenssegers\Mongodb\Relations\BelongsToMany; use Jenssegers\Mongodb\Relations\BelongsToMany;
use Jenssegers\Mongodb\Relations\MorphTo;
use Jenssegers\Mongodb\Query\Builder as QueryBuilder; use Jenssegers\Mongodb\Query\Builder as QueryBuilder;
abstract class Model extends \Illuminate\Database\Eloquent\Model { abstract class Model extends \Illuminate\Database\Eloquent\Model {
...@@ -169,6 +170,51 @@ abstract class Model extends \Illuminate\Database\Eloquent\Model { ...@@ -169,6 +170,51 @@ abstract class Model extends \Illuminate\Database\Eloquent\Model {
return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation); return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation);
} }
/**
* Define a polymorphic, inverse one-to-one or many relationship.
*
* @param string $name
* @param string $type
* @param string $id
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function morphTo($name = null, $type = null, $id = null)
{
// If no name is provided, we will use the backtrace to get the function name
// since that is most likely the name of the polymorphic interface. We can
// use that to get both the class and foreign key that will be utilized.
if (is_null($name))
{
list(, $caller) = debug_backtrace(false);
$name = snake_case($caller['function']);
}
list($type, $id) = $this->getMorphs($name, $type, $id);
// If the type value is null it is probably safe to assume we're eager loading
// the relationship. When that is the case we will pass in a dummy query as
// there are multiple types in the morph and we can't use single queries.
if (is_null($class = $this->$type))
{
return new MorphTo(
$this->newQuery(), $this, $id, null, $type, $name
);
}
// If we are not eager loading the relatinship, we will essentially treat this
// as a belongs-to style relationship since morph-to extends that class and
// we will pass in the appropriate values so that it behaves as expected.
else
{
$instance = new $class;
return new MorphTo(
with($instance)->newQuery(), $this, $id, $instance->getKeyName(), $type, $name
);
}
}
/** /**
* Define a many-to-many relationship. * Define a many-to-many relationship.
* *
......
...@@ -173,6 +173,16 @@ class Connection extends \Illuminate\Database\Connection { ...@@ -173,6 +173,16 @@ class Connection extends \Illuminate\Database\Connection {
return parent::getElapsedTime($start); return parent::getElapsedTime($start);
} }
/**
* Get the PDO driver name.
*
* @return string
*/
public function getDriverName()
{
return '';
}
/** /**
* Dynamically pass methods to the connection. * Dynamically pass methods to the connection.
* *
......
<?php namespace Jenssegers\Mongodb\Eloquent; <?php namespace Jenssegers\Mongodb\Eloquent;
use MongoCursor;
class Builder extends \Illuminate\Database\Eloquent\Builder { class Builder extends \Illuminate\Database\Eloquent\Builder {
/** /**
...@@ -12,4 +14,50 @@ class Builder extends \Illuminate\Database\Eloquent\Builder { ...@@ -12,4 +14,50 @@ class Builder extends \Illuminate\Database\Eloquent\Builder {
'count', 'min', 'max', 'avg', 'sum', 'exists', 'push', 'pull' 'count', 'min', 'max', 'avg', 'sum', 'exists', 'push', 'pull'
); );
/**
* Create a raw database expression.
*
* @param closure $expression
* @return mixed
*/
public function raw($expression = null)
{
// Get raw results from the query builder.
$results = $this->query->raw($expression);
$connection = $this->model->getConnectionName();
// Convert MongoCursor results to a collection of models.
if ($results instanceof MongoCursor)
{
$results = iterator_to_array($results, false);
$models = array();
// Once we have the results, we can spin through them and instantiate a fresh
// model instance for each records we retrieved from the database. We will
// also set the proper connection name for the model after we create it.
foreach ($results as $result)
{
$models[] = $model = $this->model->newFromBuilder($result);
$model->setConnection($connection);
}
return $this->model->newCollection($models);
}
// The result is a single object.
else if (is_array($results) and array_key_exists('_id', $results))
{
$model = $this->model->newFromBuilder($results);
$model->setConnection($connection);
return $model;
}
return $results;
}
} }
...@@ -228,7 +228,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -228,7 +228,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
} }
/** /**
* Pass push to the query builder. * Append one or more values to an array.
* *
* @return mixed * @return mixed
*/ */
...@@ -236,7 +236,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -236,7 +236,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
{ {
if ($parameters = func_get_args()) if ($parameters = func_get_args())
{ {
$query = $this->newQuery(); $query = $this->setKeysForSaveQuery($this->newQuery());
return call_user_func_array(array($query, 'push'), $parameters); return call_user_func_array(array($query, 'push'), $parameters);
} }
...@@ -244,6 +244,18 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -244,6 +244,18 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
return parent::push(); return parent::push();
} }
/**
* Remove one or more values from an array.
*
* @return mixed
*/
public function pull()
{
$query = $this->setKeysForSaveQuery($this->newQuery());
return call_user_func_array(array($query, 'pull'), func_get_args());
}
/** /**
* Create a new Eloquent query builder for the model. * Create a new Eloquent query builder for the model.
* *
......
...@@ -63,7 +63,7 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -63,7 +63,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
* @param array $columns * @param array $columns
* @return mixed * @return mixed
*/ */
public function find($id, $columns = array('*')) public function find($id, $columns = array())
{ {
return $this->where('_id', '=', $this->convertKey($id))->first($columns); return $this->where('_id', '=', $this->convertKey($id))->first($columns);
} }
...@@ -74,7 +74,7 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -74,7 +74,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
* @param array $columns * @param array $columns
* @return array|static[] * @return array|static[]
*/ */
public function getFresh($columns = array('*')) public function getFresh($columns = array())
{ {
$start = microtime(true); $start = microtime(true);
...@@ -83,68 +83,69 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -83,68 +83,69 @@ class Builder extends \Illuminate\Database\Query\Builder {
// all of the columns on the table using the "wildcard" column character. // all of the columns on the table using the "wildcard" column character.
if (is_null($this->columns)) $this->columns = $columns; if (is_null($this->columns)) $this->columns = $columns;
// Drop all columns if * is present, MongoDB does not work this way // Drop all columns if * is present, MongoDB does not work this way.
if (in_array('*', $this->columns)) $this->columns = array(); if (in_array('*', $this->columns)) $this->columns = array();
// Compile wheres // Compile wheres
$wheres = $this->compileWheres(); $wheres = $this->compileWheres();
// Aggregation query // Use MongoDB's aggregation framework when using grouping or aggregation functions.
if ($this->groups || $this->aggregate) if ($this->groups || $this->aggregate)
{ {
$group = array(); $group = array();
// Apply grouping // Add grouping columns to the $group part of the aggregation pipeline.
if ($this->groups) if ($this->groups)
{ {
foreach ($this->groups as $column) foreach ($this->groups as $column)
{ {
// Mark column as grouped
$group['_id'][$column] = '$' . $column; $group['_id'][$column] = '$' . $column;
// Aggregate by $last when grouping, this mimics MySQL's behaviour a bit // When grouping, also add the $last operator to each grouped field,
// this mimics MySQL's behaviour a bit.
$group[$column] = array('$last' => '$' . $column); $group[$column] = array('$last' => '$' . $column);
} }
} }
else else
{ {
// If we don't use grouping, set the _id to null to prepare the pipeline for // If we don't use grouping, set the _id to null to prepare the pipeline for
// other aggregation functions // other aggregation functions.
$group['_id'] = null; $group['_id'] = null;
} }
// When additional columns are requested, aggregate them by $last as well // Add aggregation functions to the $group part of the aggregation pipeline,
foreach ($this->columns as $column) // these may override previous aggregations.
{
// Replace possible dots in subdocument queries with underscores
$key = str_replace('.', '_', $column);
$group[$key] = array('$last' => '$' . $column);
}
// Apply aggregation functions, these may override previous aggregations
if ($this->aggregate) if ($this->aggregate)
{ {
$function = $this->aggregate['function']; $function = $this->aggregate['function'];
foreach ($this->aggregate['columns'] as $column) foreach ($this->aggregate['columns'] as $column)
{ {
// Replace possible dots in subdocument queries with underscores // Translate count into sum.
$key = str_replace('.', '_', $column);
// Translate count into sum
if ($function == 'count') if ($function == 'count')
{ {
$group[$key] = array('$sum' => 1); $group['aggregate'] = array('$sum' => 1);
} }
// Pass other functions directly // Pass other functions directly.
else else
{ {
$group[$key] = array('$' . $function => '$' . $column); $group['aggregate'] = array('$' . $function => '$' . $column);
} }
} }
} }
// Build pipeline // If no aggregation functions are used, we add the additional select columns
// to the pipeline here, aggregating them by $last.
else
{
foreach ($this->columns as $column)
{
$key = str_replace('.', '_', $column);
$group[$key] = array('$last' => '$' . $column);
}
}
// Build the aggregation pipeline.
$pipeline = array(); $pipeline = array();
if ($wheres) $pipeline[] = array('$match' => $wheres); if ($wheres) $pipeline[] = array('$match' => $wheres);
$pipeline[] = array('$group' => $group); $pipeline[] = array('$group' => $group);
...@@ -238,7 +239,7 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -238,7 +239,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
* @param array $columns * @param array $columns
* @return mixed * @return mixed
*/ */
public function aggregate($function, $columns = array('*')) public function aggregate($function, $columns = array())
{ {
$this->aggregate = compact('function', 'columns'); $this->aggregate = compact('function', 'columns');
...@@ -251,9 +252,9 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -251,9 +252,9 @@ class Builder extends \Illuminate\Database\Query\Builder {
if (isset($results[0])) if (isset($results[0]))
{ {
// Replace possible dots in subdocument queries with underscores $result = (array) $results[0];
$key = str_replace('.', '_', $columns[0]);
return $results[0][$key]; return $result['aggregate'];
} }
} }
...@@ -283,7 +284,16 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -283,7 +284,16 @@ class Builder extends \Illuminate\Database\Query\Builder {
*/ */
public function orderBy($column, $direction = 'asc') public function orderBy($column, $direction = 'asc')
{ {
$this->orders[$column] = (strtolower($direction) == 'asc' ? 1 : -1); $direction = (strtolower($direction) == 'asc' ? 1 : -1);
if ($column == 'natural')
{
$this->orders['$natural'] = $direction;
}
else
{
$this->orders[$column] = $direction;
}
return $this; return $this;
} }
...@@ -664,6 +674,12 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -664,6 +674,12 @@ class Builder extends \Illuminate\Database\Query\Builder {
foreach ($this->wheres as $i => &$where) foreach ($this->wheres as $i => &$where)
{ {
// Make sure the operator is in lowercase
if (isset($where['operator']))
{
$where['operator'] = strtolower($where['operator']);
}
// Convert id's // Convert id's
if (isset($where['column']) && $where['column'] == '_id') if (isset($where['column']) && $where['column'] == '_id')
{ {
......
...@@ -36,9 +36,7 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -36,9 +36,7 @@ class BelongsToMany extends EloquentBelongsToMany {
{ {
if (static::$constraints) if (static::$constraints)
{ {
// Make sure that the primary key of the parent $this->query->where($this->foreignKey, $this->parent->getKey());
// is in the relationship array of keys
$this->query->whereIn($this->foreignKey, array($this->parent->getKey()));
} }
} }
...@@ -114,15 +112,6 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -114,15 +112,6 @@ class BelongsToMany extends EloquentBelongsToMany {
{ {
if ($id instanceof Model) $id = $id->getKey(); if ($id instanceof Model) $id = $id->getKey();
// Generate a new parent query instance
$parent = $this->newParentQuery();
// Generate a new related query instance
$related = $this->related->newInstance();
// Set contraints on the related query
$related = $related->where($this->related->getKeyName(), $id);
$records = $this->createAttachRecords((array) $id, $attributes); $records = $this->createAttachRecords((array) $id, $attributes);
// Get the ID's to attach to the two documents // Get the ID's to attach to the two documents
...@@ -130,10 +119,18 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -130,10 +119,18 @@ class BelongsToMany extends EloquentBelongsToMany {
$foreignIds = array_pluck($records, $this->foreignKey); $foreignIds = array_pluck($records, $this->foreignKey);
// Attach to the parent model // Attach to the parent model
$parent->push($this->otherKey, $otherIds[0]); $this->parent->push($this->otherKey, $otherIds[0]);
// Generate a new related query instance
$query = $this->getNewRelatedQuery();
// Set contraints on the related query
$query->where($this->related->getKeyName(), $id);
// Attach to the related model // Attach to the related model
$related->push($this->foreignKey, $foreignIds[0]); $query->push($this->foreignKey, $foreignIds[0]);
if ($touch) $this->touchIfTouching();
} }
/** /**
...@@ -168,47 +165,32 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -168,47 +165,32 @@ class BelongsToMany extends EloquentBelongsToMany {
{ {
if ($ids instanceof Model) $ids = (array) $ids->getKey(); if ($ids instanceof Model) $ids = (array) $ids->getKey();
$query = $this->newParentQuery();
// Generate a new related query instance
$related = $this->related->newInstance();
// If associated IDs were passed to the method we will only delete those // If associated IDs were passed to the method we will only delete those
// associations, otherwise all of the association ties will be broken. // associations, otherwise all of the association ties will be broken.
// We'll return the numbers of affected rows when we do the deletes. // We'll return the numbers of affected rows when we do the deletes.
$ids = (array) $ids; $ids = (array) $ids;
if (count($ids) > 0) // Pull each id from the parent.
foreach ($ids as $id)
{ {
$query->whereIn($this->otherKey, $ids); $this->parent->pull($this->otherKey, $id);
} }
if ($touch) $this->touchIfTouching(); // Get a new related query.
$query = $this->getNewRelatedQuery();
// Once we have all of the conditions set on the statement, we are ready // Prepare the query to select all related objects.
// to run the delete on the pivot table. Then, if the touch parameter if (count($ids) > 0)
// is true, we will go ahead and touch all related models to sync.
foreach ($ids as $id)
{ {
$query->pull($this->otherKey, $id); $query->whereIn($this->related->getKeyName(), $ids);
} }
// Remove the relation from the related model // Remove the relation to the parent.
$related->pull($this->foreignKey, $this->parent->getKey()); $query->pull($this->foreignKey, $this->parent->getKey());
return count($ids);
}
/** if ($touch) $this->touchIfTouching();
* Create a new query builder for the parent
*
* @return Jenssegers\Mongodb\Builder
*/
protected function newParentQuery()
{
$query = $this->parent->newQuery();
return $query->where($this->parent->getKeyName(), '=', $this->parent->getKey()); return count($ids);
} }
/** /**
...@@ -237,6 +219,16 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -237,6 +219,16 @@ class BelongsToMany extends EloquentBelongsToMany {
return $dictionary; return $dictionary;
} }
/**
* Get a new related query.
*
* @return \Illuminate\Database\Query\Builder
*/
public function getNewRelatedQuery()
{
return $this->related->newQuery();
}
/** /**
* Get the fully qualified foreign key for the relation. * Get the fully qualified foreign key for the relation.
* *
......
<?php namespace Jenssegers\Mongodb\Relations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Expression;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Collection as BaseCollection;
class MorphTo extends BelongsTo {
/**
* The type of the polymorphic relation.
*
* @var string
*/
protected $morphType;
/**
* The models whose relations are being eager loaded.
*
* @var \Illuminate\Database\Eloquent\Collection
*/
protected $models;
/**
* All of the models keyed by ID.
*
* @var array
*/
protected $dictionary = array();
/**
* Create a new belongs to relationship instance.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \Illuminate\Database\Eloquent\Model $parent
* @param string $foreignKey
* @param string $otherKey
* @param string $type
* @param string $relation
* @return void
*/
public function __construct(Builder $query, Model $parent, $foreignKey, $otherKey, $type, $relation)
{
$this->morphType = $type;
parent::__construct($query, $parent, $foreignKey, $otherKey, $relation);
}
/**
* Set the constraints for an eager load of the relation.
*
* @param array $models
* @return void
*/
public function addEagerConstraints(array $models)
{
$this->buildDictionary($this->models = Collection::make($models));
}
/**
* Buiild a dictionary with the models.
*
* @param \Illuminate\Database\Eloquent\Models $models
* @return void
*/
protected function buildDictionary(Collection $models)
{
foreach ($models as $model)
{
if ($model->{$this->morphType})
{
$this->dictionary[$model->{$this->morphType}][$model->{$this->foreignKey}][] = $model;
}
}
}
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
*/
public function match(array $models, Collection $results, $relation)
{
return $models;
}
/**
* Get the results of the relationship.
*
* Called via eager load method of Eloquent query builder.
*
* @return mixed
*/
public function getEager()
{
foreach (array_keys($this->dictionary) as $type)
{
$this->matchToMorphParents($type, $this->getResultsByType($type));
}
return $this->models;
}
/**
* Match the results for a given type to their parents.
*
* @param string $type
* @param \Illuminate\Database\Eloquent\Collection $results
* @return void
*/
protected function matchToMorphParents($type, Collection $results)
{
foreach ($results as $result)
{
if (isset($this->dictionary[$type][$result->getKey()]))
{
foreach ($this->dictionary[$type][$result->getKey()] as $model)
{
$model->setRelation($this->relation, $result);
}
}
}
}
/**
* Get all of the relation results for a type.
*
* @param string $type
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function getResultsByType($type)
{
$instance = $this->createModelByType($type);
$key = $instance->getKeyName();
return $instance->whereIn($key, $this->gatherKeysByType($type)->all())->get();
}
/**
* Gather all of the foreign keys for a given type.
*
* @param string $type
* @return array
*/
protected function gatherKeysByType($type)
{
$foreign = $this->foreignKey;
return BaseCollection::make($this->dictionary[$type])->map(function($models) use ($foreign)
{
return head($models)->{$foreign};
})->unique();
}
/**
* Create a new model instance by type.
*
* @param string $type
* @return \Illuminate\Database\Eloquent\Model
*/
public function createModelByType($type)
{
return new $type;
}
/**
* Get the dictionary used by the relationship.
*
* @return array
*/
public function getDictionary()
{
return $this->dictionary;
}
}
...@@ -81,4 +81,10 @@ class ConnectionTest extends PHPUnit_Framework_TestCase { ...@@ -81,4 +81,10 @@ class ConnectionTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(5, count(DB::getQueryLog())); $this->assertEquals(5, count(DB::getQueryLog()));
} }
public function testSchemaBuilder()
{
$schema = DB::connection('mongodb')->getSchemaBuilder();
$this->assertInstanceOf('Jenssegers\Mongodb\Schema\Builder', $schema);
}
} }
...@@ -164,11 +164,9 @@ class ModelTest extends PHPUnit_Framework_TestCase { ...@@ -164,11 +164,9 @@ class ModelTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(null, $item); $this->assertEquals(null, $item);
} }
/**
* @expectedException Illuminate\Database\Eloquent\ModelNotFoundException
*/
public function testFindOrfail() public function testFindOrfail()
{ {
$this->setExpectedException('Illuminate\Database\Eloquent\ModelNotFoundException');
User::findOrfail('51c33d8981fec6813e00000a'); User::findOrfail('51c33d8981fec6813e00000a');
} }
...@@ -344,4 +342,36 @@ class ModelTest extends PHPUnit_Framework_TestCase { ...@@ -344,4 +342,36 @@ class ModelTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(1, count($user->tags)); $this->assertEquals(1, count($user->tags));
} }
public function testRaw()
{
User::create(array('name' => 'John Doe', 'age' => 35));
User::create(array('name' => 'Jane Doe', 'age' => 35));
User::create(array('name' => 'Harry Hoe', 'age' => 15));
$users = User::raw(function($collection)
{
return $collection->find(array('age' => 35));
});
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $users);
$this->assertInstanceOf('Jenssegers\Mongodb\Model', $users[0]);
$user = User::raw(function($collection)
{
return $collection->findOne(array('age' => 35));
});
$this->assertInstanceOf('Jenssegers\Mongodb\Model', $user);
$count = User::raw(function($collection)
{
return $collection->count();
});
$this->assertEquals(3, $count);
$result = User::raw(function($collection)
{
return $collection->insert(array('name' => 'Yvonne Yoe', 'age' => 35));
});
$this->assertTrue(is_array($result));
}
} }
...@@ -185,7 +185,8 @@ class QueryBuilderTest extends PHPUnit_Framework_TestCase { ...@@ -185,7 +185,8 @@ class QueryBuilderTest extends PHPUnit_Framework_TestCase {
array('name' => 'John Doe', 'age' => 25) array('name' => 'John Doe', 'age' => 25)
)); ));
$cursor = DB::collection('users')->raw(function($collection) { $cursor = DB::collection('users')->raw(function($collection)
{
return $collection->find(array('age' => 20)); return $collection->find(array('age' => 20));
}); });
...@@ -195,6 +196,9 @@ class QueryBuilderTest extends PHPUnit_Framework_TestCase { ...@@ -195,6 +196,9 @@ class QueryBuilderTest extends PHPUnit_Framework_TestCase {
$collection = DB::collection('users')->raw(); $collection = DB::collection('users')->raw();
$this->assertInstanceOf('MongoCollection', $collection); $this->assertInstanceOf('MongoCollection', $collection);
$collection = User::raw();
$this->assertInstanceOf('MongoCollection', $collection);
$results = DB::collection('users')->whereRaw(array('age' => 20))->get(); $results = DB::collection('users')->whereRaw(array('age' => 20))->get();
$this->assertEquals(1, count($results)); $this->assertEquals(1, count($results));
$this->assertEquals('Jane Doe', $results[0]['name']); $this->assertEquals('Jane Doe', $results[0]['name']);
...@@ -517,6 +521,9 @@ class QueryBuilderTest extends PHPUnit_Framework_TestCase { ...@@ -517,6 +521,9 @@ class QueryBuilderTest extends PHPUnit_Framework_TestCase {
$regex = new MongoRegex("/.*doe/i"); $regex = new MongoRegex("/.*doe/i");
$results = DB::collection('users')->where('name', 'regex', $regex)->get(); $results = DB::collection('users')->where('name', 'regex', $regex)->get();
$this->assertEquals(2, count($results)); $this->assertEquals(2, count($results));
$results = DB::collection('users')->where('name', 'REGEX', $regex)->get();
$this->assertEquals(2, count($results));
} }
public function testIncrement() public function testIncrement()
......
...@@ -64,6 +64,9 @@ class QueryTest extends PHPUnit_Framework_TestCase { ...@@ -64,6 +64,9 @@ class QueryTest extends PHPUnit_Framework_TestCase {
$users = User::where('name', 'like', '%y%')->get(); $users = User::where('name', 'like', '%y%')->get();
$this->assertEquals(3, count($users)); $this->assertEquals(3, count($users));
$users = User::where('name', 'LIKE', '%y%')->get();
$this->assertEquals(3, count($users));
$users = User::where('name', 'like', 't%')->get(); $users = User::where('name', 'like', 't%')->get();
$this->assertEquals(1, count($users)); $this->assertEquals(1, count($users));
} }
...@@ -74,6 +77,7 @@ class QueryTest extends PHPUnit_Framework_TestCase { ...@@ -74,6 +77,7 @@ class QueryTest extends PHPUnit_Framework_TestCase {
$this->assertEquals('John Doe', $user->name); $this->assertEquals('John Doe', $user->name);
$this->assertEquals(null, $user->age); $this->assertEquals(null, $user->age);
$this->assertEquals(null, $user->title);
$user = User::where('name', 'John Doe')->select('name', 'title')->first(); $user = User::where('name', 'John Doe')->select('name', 'title')->first();
...@@ -81,6 +85,12 @@ class QueryTest extends PHPUnit_Framework_TestCase { ...@@ -81,6 +85,12 @@ class QueryTest extends PHPUnit_Framework_TestCase {
$this->assertEquals('admin', $user->title); $this->assertEquals('admin', $user->title);
$this->assertEquals(null, $user->age); $this->assertEquals(null, $user->age);
$user = User::where('name', 'John Doe')->select(array('name', 'title'))->get()->first();
$this->assertEquals('John Doe', $user->name);
$this->assertEquals('admin', $user->title);
$this->assertEquals(null, $user->age);
$user = User::where('name', 'John Doe')->get(array('name'))->first(); $user = User::where('name', 'John Doe')->get(array('name'))->first();
$this->assertEquals('John Doe', $user->name); $this->assertEquals('John Doe', $user->name);
...@@ -147,6 +157,15 @@ class QueryTest extends PHPUnit_Framework_TestCase { ...@@ -147,6 +157,15 @@ class QueryTest extends PHPUnit_Framework_TestCase {
$user = User::whereNotNull('age')->orderBy('age', 'desc')->first(); $user = User::whereNotNull('age')->orderBy('age', 'desc')->first();
$this->assertEquals(37, $user->age); $this->assertEquals(37, $user->age);
$user = User::whereNotNull('age')->orderBy('natural', 'asc')->first();
$this->assertEquals(35, $user->age);
$user = User::whereNotNull('age')->orderBy('natural', 'ASC')->first();
$this->assertEquals(35, $user->age);
$user = User::whereNotNull('age')->orderBy('natural', 'desc')->first();
$this->assertEquals(35, $user->age);
} }
public function testGroupBy() public function testGroupBy()
...@@ -173,6 +192,16 @@ class QueryTest extends PHPUnit_Framework_TestCase { ...@@ -173,6 +192,16 @@ class QueryTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(33, $users[1]->age); $this->assertEquals(33, $users[1]->age);
} }
public function testCount()
{
$count = User::where('age', '<>', 35)->count();
$this->assertEquals(6, $count);
// Test for issue #165
$count = User::select('_id', 'age', 'title')->where('age', '<>', 35)->count();
$this->assertEquals(6, $count);
}
public function testSubquery() public function testSubquery()
{ {
$users = User::where('title', 'admin')->orWhere(function($query) $users = User::where('title', 'admin')->orWhere(function($query)
...@@ -203,7 +232,7 @@ class QueryTest extends PHPUnit_Framework_TestCase { ...@@ -203,7 +232,7 @@ class QueryTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(5, count($users)); $this->assertEquals(5, count($users));
} }
public function testRaw() public function testWhereRaw()
{ {
$where = array('age' => array('$gt' => 30, '$lt' => 40)); $where = array('age' => array('$gt' => 30, '$lt' => 40));
$users = User::whereRaw($where)->get(); $users = User::whereRaw($where)->get();
......
...@@ -9,6 +9,8 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -9,6 +9,8 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
public function tearDown() public function tearDown()
{ {
Mockery::close();
User::truncate(); User::truncate();
Book::truncate(); Book::truncate();
Item::truncate(); Item::truncate();
...@@ -274,8 +276,17 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -274,8 +276,17 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(1, $user->photos->count()); $this->assertEquals(1, $user->photos->count());
$this->assertEquals($photo->id, $user->photos->first()->id); $this->assertEquals($photo->id, $user->photos->first()->id);
$user = User::find($user->_id);
$this->assertEquals(1, $user->photos->count());
$this->assertEquals($photo->id, $user->photos->first()->id);
$photo = Photo::create(array('url' => 'http://graph.facebook.com/john.doe/picture')); $photo = Photo::create(array('url' => 'http://graph.facebook.com/john.doe/picture'));
$client->photos()->save($photo); $client->photos()->save($photo);
$this->assertEquals(1, $client->photos->count());
$this->assertEquals($photo->id, $client->photos->first()->id);
$client = Client::find($client->_id);
$this->assertEquals(1, $client->photos->count()); $this->assertEquals(1, $client->photos->count());
$this->assertEquals($photo->id, $client->photos->first()->id); $this->assertEquals($photo->id, $client->photos->first()->id);
...@@ -288,7 +299,15 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -288,7 +299,15 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London')); $address = new Address(array('city' => 'London'));
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.creating: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.created: '.get_class($address), $address);
$events->shouldReceive('fire')->once()->with('eloquent.saved: '.get_class($address), $address);
$address = $user->addresses()->save($address); $address = $user->addresses()->save($address);
$address->unsetEventDispatcher();
$this->assertNotNull($user->_addresses); $this->assertNotNull($user->_addresses);
$this->assertEquals(array('London'), $user->addresses->lists('city')); $this->assertEquals(array('London'), $user->addresses->lists('city'));
$this->assertInstanceOf('DateTime', $address->created_at); $this->assertInstanceOf('DateTime', $address->created_at);
...@@ -300,8 +319,15 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -300,8 +319,15 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$user = User::find($user->_id); $user = User::find($user->_id);
$this->assertEquals(array('London', 'Paris'), $user->addresses->lists('city')); $this->assertEquals(array('London', 'Paris'), $user->addresses->lists('city'));
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.updating: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.updated: '.get_class($address), $address);
$events->shouldReceive('fire')->once()->with('eloquent.saved: '.get_class($address), $address);
$address->city = 'New York'; $address->city = 'New York';
$user->addresses()->save($address); $user->addresses()->save($address);
$address->unsetEventDispatcher();
$this->assertEquals(2, count($user->addresses)); $this->assertEquals(2, count($user->addresses));
$this->assertEquals(2, count($user->addresses()->get())); $this->assertEquals(2, count($user->addresses()->get()));
...@@ -329,6 +355,27 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -329,6 +355,27 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(array('London', 'Manhattan', 'Bruxelles'), $freshUser->addresses->lists('city')); $this->assertEquals(array('London', 'Manhattan', 'Bruxelles'), $freshUser->addresses->lists('city'));
} }
public function testEmbedsManyAssociate()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London'));
$address = $user->addresses()->associate($address);
$this->assertNotNull($user->_addresses);
$this->assertEquals(array('London'), $user->addresses->lists('city'));
$this->assertNotNull($address->_id);
$freshUser = User::find($user->_id);
$this->assertEquals(array(), $freshUser->addresses->lists('city'));
$address->city = 'Londinium';
$user->addresses()->associate($address);
$this->assertEquals(array('Londinium'), $user->addresses->lists('city'));
$freshUser = User::find($user->_id);
$this->assertEquals(array(), $freshUser->addresses->lists('city'));
}
public function testEmbedsManySaveMany() public function testEmbedsManySaveMany()
{ {
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
...@@ -373,9 +420,16 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -373,9 +420,16 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$user->addresses()->saveMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol')), new Address(array('city' => 'Bruxelles')))); $user->addresses()->saveMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol')), new Address(array('city' => 'Bruxelles'))));
$address = $user->addresses->first(); $address = $user->addresses->first();
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.deleting: '.get_class($address), Mockery::mustBe($address))->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.deleted: '.get_class($address), Mockery::mustBe($address));
$user->addresses()->destroy($address->_id); $user->addresses()->destroy($address->_id);
$this->assertEquals(array('Bristol', 'Bruxelles'), $user->addresses->lists('city')); $this->assertEquals(array('Bristol', 'Bruxelles'), $user->addresses->lists('city'));
$address->unsetEventDispatcher();
$address = $user->addresses->first(); $address = $user->addresses->first();
$user->addresses()->destroy($address); $user->addresses()->destroy($address);
$this->assertEquals(array('Bruxelles'), $user->addresses->lists('city')); $this->assertEquals(array('Bruxelles'), $user->addresses->lists('city'));
...@@ -383,8 +437,8 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -383,8 +437,8 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$user->addresses()->create(array('city' => 'Paris')); $user->addresses()->create(array('city' => 'Paris'));
$user->addresses()->create(array('city' => 'San Francisco')); $user->addresses()->create(array('city' => 'San Francisco'));
$user = User::find($user->id); $freshUser = User::find($user->id);
$this->assertEquals(array('Bruxelles', 'Paris', 'San Francisco'), $user->addresses->lists('city')); $this->assertEquals(array('Bruxelles', 'Paris', 'San Francisco'), $freshUser->addresses->lists('city'));
$ids = $user->addresses->lists('_id'); $ids = $user->addresses->lists('_id');
$user->addresses()->destroy($ids); $user->addresses()->destroy($ids);
...@@ -392,6 +446,22 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -392,6 +446,22 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$freshUser = User::find($user->id); $freshUser = User::find($user->id);
$this->assertEquals(array(), $freshUser->addresses->lists('city')); $this->assertEquals(array(), $freshUser->addresses->lists('city'));
list($london, $bristol, $bruxelles) = $user->addresses()->saveMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol')), new Address(array('city' => 'Bruxelles'))));
$user->addresses()->destroy(array($london, $bruxelles));
$this->assertEquals(array('Bristol'), $user->addresses->lists('city'));
}
public function testEmbedsManyDissociate()
{
$user = User::create(array());
$cordoba = $user->addresses()->create(array('city' => 'Cordoba'));
$user->addresses()->dissociate($cordoba->id);
$freshUser = User::find($user->id);
$this->assertEquals(0, $user->addresses->count());
$this->assertEquals(1, $freshUser->addresses->count());
} }
public function testEmbedsManyAliases() public function testEmbedsManyAliases()
...@@ -406,4 +476,62 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -406,4 +476,62 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(array(), $user->addresses->lists('city')); $this->assertEquals(array(), $user->addresses->lists('city'));
} }
public function testEmbedsManyCreatingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London'));
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.creating: '.get_class($address), $address)->andReturn(false);
$this->assertFalse($user->addresses()->save($address));
$address->unsetEventDispatcher();
}
public function testEmbedsManySavingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'Paris'));
$address->exists = true;
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(false);
$this->assertFalse($user->addresses()->save($address));
$address->unsetEventDispatcher();
}
public function testEmbedsManyUpdatingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'New York'));
$address->exists = true;
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.updating: '.get_class($address), $address)->andReturn(false);
$address->city = 'Warsaw';
$this->assertFalse($user->addresses()->save($address));
$address->unsetEventDispatcher();
}
public function testEmbedsManyDeletingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$user->addresses()->save(new Address(array('city' => 'New York')));
$address = $user->addresses->first();
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.deleting: '.get_class($address), Mockery::mustBe($address))->andReturn(false);
$this->assertEquals(0, $user->addresses()->destroy($address));
$this->assertEquals(array('New York'), $user->addresses->lists('city'));
$address->unsetEventDispatcher();
}
} }
...@@ -16,6 +16,7 @@ class SchemaTest extends PHPUnit_Framework_TestCase { ...@@ -16,6 +16,7 @@ class SchemaTest extends PHPUnit_Framework_TestCase {
{ {
Schema::create('newcollection'); Schema::create('newcollection');
$this->assertTrue(Schema::hasCollection('newcollection')); $this->assertTrue(Schema::hasCollection('newcollection'));
$this->assertTrue(Schema::hasTable('newcollection'));
} }
public function testDrop() public function testDrop()
...@@ -25,6 +26,21 @@ class SchemaTest extends PHPUnit_Framework_TestCase { ...@@ -25,6 +26,21 @@ class SchemaTest extends PHPUnit_Framework_TestCase {
$this->assertFalse(Schema::hasCollection('newcollection')); $this->assertFalse(Schema::hasCollection('newcollection'));
} }
public function testBluePrint()
{
$instance = $this;
Schema::collection('newcollection', function($collection) use ($instance)
{
$instance->assertInstanceOf('Jenssegers\Mongodb\Schema\Blueprint', $collection);
});
Schema::table('newcollection', function($collection) use ($instance)
{
$instance->assertInstanceOf('Jenssegers\Mongodb\Schema\Blueprint', $collection);
});
}
public function testIndex() public function testIndex()
{ {
Schema::collection('newcollection', function($collection) Schema::collection('newcollection', function($collection)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment