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

Merge master

parents a54ef6d9 5b7f335a
......@@ -16,6 +16,12 @@ before_script:
- echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- mysql -e 'create database unittest;'
- composer self-update
- composer require satooshi/php-coveralls:dev-master
- 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
===============
[![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.*
......@@ -44,7 +44,7 @@ You can connect to multiple servers or replica sets with the following configura
'mongodb' => array(
'driver' => 'mongodb',
'host' => array('server1', 'server2),
'host' => array('server1', 'server2'),
'port' => 27017,
'username' => 'username',
'password' => 'password',
......@@ -414,6 +414,11 @@ You can remove an embedded document by using the `destroy()` method:
// or
$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:
return $this->embedsMany('Book', 'local_key');
......@@ -458,16 +463,23 @@ These expressions will be injected directly into the query.
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();
});
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:
......
......@@ -17,13 +17,13 @@
},
"require-dev": {
"illuminate/cache": "4.2.x",
"illuminate/auth": "4.2.x"
"illuminate/auth": "4.2.x",
"mockery/mockery": "*"
},
"autoload": {
"psr-0": {
"Jenssegers\\Mongodb": "src/",
"Jenssegers\\Eloquent": "src/"
}
},
"minimum-stability": "dev"
}
}
......@@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\Relation;
use Jenssegers\Mongodb\Relations\BelongsTo;
use Jenssegers\Mongodb\Relations\BelongsToMany;
use Jenssegers\Mongodb\Relations\MorphTo;
use Jenssegers\Mongodb\Query\Builder as QueryBuilder;
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);
}
/**
* 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.
*
......
......@@ -173,6 +173,16 @@ class Connection extends \Illuminate\Database\Connection {
return parent::getElapsedTime($start);
}
/**
* Get the PDO driver name.
*
* @return string
*/
public function getDriverName()
{
return '';
}
/**
* Dynamically pass methods to the connection.
*
......
<?php namespace Jenssegers\Mongodb\Eloquent;
use MongoCursor;
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'
);
/**
* 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 {
}
/**
* Pass push to the query builder.
* Append one or more values to an array.
*
* @return mixed
*/
......@@ -236,7 +236,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
{
if ($parameters = func_get_args())
{
$query = $this->newQuery();
$query = $this->setKeysForSaveQuery($this->newQuery());
return call_user_func_array(array($query, 'push'), $parameters);
}
......@@ -244,6 +244,18 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
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.
*
......
......@@ -63,7 +63,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
* @param array $columns
* @return mixed
*/
public function find($id, $columns = array('*'))
public function find($id, $columns = array())
{
return $this->where('_id', '=', $this->convertKey($id))->first($columns);
}
......@@ -74,7 +74,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
* @param array $columns
* @return array|static[]
*/
public function getFresh($columns = array('*'))
public function getFresh($columns = array())
{
$start = microtime(true);
......@@ -83,68 +83,69 @@ class Builder extends \Illuminate\Database\Query\Builder {
// all of the columns on the table using the "wildcard" column character.
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();
// Compile wheres
$wheres = $this->compileWheres();
// Aggregation query
// Use MongoDB's aggregation framework when using grouping or aggregation functions.
if ($this->groups || $this->aggregate)
{
$group = array();
// Apply grouping
// Add grouping columns to the $group part of the aggregation pipeline.
if ($this->groups)
{
foreach ($this->groups as $column)
{
// Mark column as grouped
$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);
}
}
else
{
// 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;
}
// When additional columns are requested, aggregate them by $last as well
foreach ($this->columns as $column)
{
// 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
// Add aggregation functions to the $group part of the aggregation pipeline,
// these may override previous aggregations.
if ($this->aggregate)
{
$function = $this->aggregate['function'];
foreach ($this->aggregate['columns'] as $column)
{
// Replace possible dots in subdocument queries with underscores
$key = str_replace('.', '_', $column);
// Translate count into sum
// Translate count into sum.
if ($function == 'count')
{
$group[$key] = array('$sum' => 1);
$group['aggregate'] = array('$sum' => 1);
}
// Pass other functions directly
// Pass other functions directly.
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();
if ($wheres) $pipeline[] = array('$match' => $wheres);
$pipeline[] = array('$group' => $group);
......@@ -238,7 +239,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
* @param array $columns
* @return mixed
*/
public function aggregate($function, $columns = array('*'))
public function aggregate($function, $columns = array())
{
$this->aggregate = compact('function', 'columns');
......@@ -251,9 +252,9 @@ class Builder extends \Illuminate\Database\Query\Builder {
if (isset($results[0]))
{
// Replace possible dots in subdocument queries with underscores
$key = str_replace('.', '_', $columns[0]);
return $results[0][$key];
$result = (array) $results[0];
return $result['aggregate'];
}
}
......@@ -283,7 +284,16 @@ class Builder extends \Illuminate\Database\Query\Builder {
*/
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;
}
......@@ -664,6 +674,12 @@ class Builder extends \Illuminate\Database\Query\Builder {
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
if (isset($where['column']) && $where['column'] == '_id')
{
......
......@@ -36,9 +36,7 @@ class BelongsToMany extends EloquentBelongsToMany {
{
if (static::$constraints)
{
// Make sure that the primary key of the parent
// is in the relationship array of keys
$this->query->whereIn($this->foreignKey, array($this->parent->getKey()));
$this->query->where($this->foreignKey, $this->parent->getKey());
}
}
......@@ -114,15 +112,6 @@ class BelongsToMany extends EloquentBelongsToMany {
{
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);
// Get the ID's to attach to the two documents
......@@ -130,10 +119,18 @@ class BelongsToMany extends EloquentBelongsToMany {
$foreignIds = array_pluck($records, $this->foreignKey);
// 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
$related->push($this->foreignKey, $foreignIds[0]);
$query->push($this->foreignKey, $foreignIds[0]);
if ($touch) $this->touchIfTouching();
}
/**
......@@ -168,47 +165,32 @@ class BelongsToMany extends EloquentBelongsToMany {
{
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
// associations, otherwise all of the association ties will be broken.
// We'll return the numbers of affected rows when we do the deletes.
$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
// to run the delete on the pivot table. Then, if the touch parameter
// is true, we will go ahead and touch all related models to sync.
foreach ($ids as $id)
// Prepare the query to select all related objects.
if (count($ids) > 0)
{
$query->pull($this->otherKey, $id);
$query->whereIn($this->related->getKeyName(), $ids);
}
// Remove the relation from the related model
$related->pull($this->foreignKey, $this->parent->getKey());
return count($ids);
}
// Remove the relation to the parent.
$query->pull($this->foreignKey, $this->parent->getKey());
/**
* Create a new query builder for the parent
*
* @return Jenssegers\Mongodb\Builder
*/
protected function newParentQuery()
{
$query = $this->parent->newQuery();
if ($touch) $this->touchIfTouching();
return $query->where($this->parent->getKeyName(), '=', $this->parent->getKey());
return count($ids);
}
/**
......@@ -237,6 +219,16 @@ class BelongsToMany extends EloquentBelongsToMany {
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.
*
......
<?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 {
$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 {
$this->assertEquals(null, $item);
}
/**
* @expectedException Illuminate\Database\Eloquent\ModelNotFoundException
*/
public function testFindOrfail()
{
$this->setExpectedException('Illuminate\Database\Eloquent\ModelNotFoundException');
User::findOrfail('51c33d8981fec6813e00000a');
}
......@@ -344,4 +342,36 @@ class ModelTest extends PHPUnit_Framework_TestCase {
$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 {
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));
});
......@@ -195,6 +196,9 @@ class QueryBuilderTest extends PHPUnit_Framework_TestCase {
$collection = DB::collection('users')->raw();
$this->assertInstanceOf('MongoCollection', $collection);
$collection = User::raw();
$this->assertInstanceOf('MongoCollection', $collection);
$results = DB::collection('users')->whereRaw(array('age' => 20))->get();
$this->assertEquals(1, count($results));
$this->assertEquals('Jane Doe', $results[0]['name']);
......@@ -517,6 +521,9 @@ class QueryBuilderTest extends PHPUnit_Framework_TestCase {
$regex = new MongoRegex("/.*doe/i");
$results = DB::collection('users')->where('name', 'regex', $regex)->get();
$this->assertEquals(2, count($results));
$results = DB::collection('users')->where('name', 'REGEX', $regex)->get();
$this->assertEquals(2, count($results));
}
public function testIncrement()
......
......@@ -64,6 +64,9 @@ class QueryTest extends PHPUnit_Framework_TestCase {
$users = User::where('name', 'like', '%y%')->get();
$this->assertEquals(3, count($users));
$users = User::where('name', 'LIKE', '%y%')->get();
$this->assertEquals(3, count($users));
$users = User::where('name', 'like', 't%')->get();
$this->assertEquals(1, count($users));
}
......@@ -74,6 +77,7 @@ class QueryTest extends PHPUnit_Framework_TestCase {
$this->assertEquals('John Doe', $user->name);
$this->assertEquals(null, $user->age);
$this->assertEquals(null, $user->title);
$user = User::where('name', 'John Doe')->select('name', 'title')->first();
......@@ -81,6 +85,12 @@ class QueryTest extends PHPUnit_Framework_TestCase {
$this->assertEquals('admin', $user->title);
$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();
$this->assertEquals('John Doe', $user->name);
......@@ -147,6 +157,15 @@ class QueryTest extends PHPUnit_Framework_TestCase {
$user = User::whereNotNull('age')->orderBy('age', 'desc')->first();
$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()
......@@ -173,6 +192,16 @@ class QueryTest extends PHPUnit_Framework_TestCase {
$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()
{
$users = User::where('title', 'admin')->orWhere(function($query)
......@@ -203,7 +232,7 @@ class QueryTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(5, count($users));
}
public function testRaw()
public function testWhereRaw()
{
$where = array('age' => array('$gt' => 30, '$lt' => 40));
$users = User::whereRaw($where)->get();
......
......@@ -9,6 +9,8 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
public function tearDown()
{
Mockery::close();
User::truncate();
Book::truncate();
Item::truncate();
......@@ -274,8 +276,17 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(1, $user->photos->count());
$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'));
$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($photo->id, $client->photos->first()->id);
......@@ -288,7 +299,15 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$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(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->unsetEventDispatcher();
$this->assertNotNull($user->_addresses);
$this->assertEquals(array('London'), $user->addresses->lists('city'));
$this->assertInstanceOf('DateTime', $address->created_at);
......@@ -300,8 +319,15 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$user = User::find($user->_id);
$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';
$user->addresses()->save($address);
$address->unsetEventDispatcher();
$this->assertEquals(2, count($user->addresses));
$this->assertEquals(2, count($user->addresses()->get()));
......@@ -329,6 +355,27 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$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()
{
$user = User::create(array('name' => 'John Doe'));
......@@ -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'))));
$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);
$this->assertEquals(array('Bristol', 'Bruxelles'), $user->addresses->lists('city'));
$address->unsetEventDispatcher();
$address = $user->addresses->first();
$user->addresses()->destroy($address);
$this->assertEquals(array('Bruxelles'), $user->addresses->lists('city'));
......@@ -383,8 +437,8 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$user->addresses()->create(array('city' => 'Paris'));
$user->addresses()->create(array('city' => 'San Francisco'));
$user = User::find($user->id);
$this->assertEquals(array('Bruxelles', 'Paris', 'San Francisco'), $user->addresses->lists('city'));
$freshUser = User::find($user->id);
$this->assertEquals(array('Bruxelles', 'Paris', 'San Francisco'), $freshUser->addresses->lists('city'));
$ids = $user->addresses->lists('_id');
$user->addresses()->destroy($ids);
......@@ -392,6 +446,22 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$freshUser = User::find($user->id);
$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()
......@@ -406,4 +476,62 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$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 {
{
Schema::create('newcollection');
$this->assertTrue(Schema::hasCollection('newcollection'));
$this->assertTrue(Schema::hasTable('newcollection'));
}
public function testDrop()
......@@ -25,6 +26,21 @@ class SchemaTest extends PHPUnit_Framework_TestCase {
$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()
{
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