Commit 0a256970 authored by Jens Segers's avatar Jens Segers

Merge pull request #279 from jenssegers/embedded-models

New v2 release
parents 7534e6f9 94314570
...@@ -3,7 +3,17 @@ Laravel MongoDB ...@@ -3,7 +3,17 @@ Laravel MongoDB
[![Latest Stable Version](http://img.shields.io/github/release/jenssegers/laravel-mongodb.svg)](https://packagist.org/packages/jenssegers/mongodb) [![Total Downloads](http://img.shields.io/packagist/dm/jenssegers/mongodb.svg)](https://packagist.org/packages/jenssegers/mongodb) [![Build Status](http://img.shields.io/travis/jenssegers/laravel-mongodb.svg)](https://travis-ci.org/jenssegers/laravel-mongodb) [![Coverage Status](http://img.shields.io/coveralls/jenssegers/laravel-mongodb.svg)](https://coveralls.io/r/jenssegers/laravel-mongodb?branch=master) [![Latest Stable Version](http://img.shields.io/github/release/jenssegers/laravel-mongodb.svg)](https://packagist.org/packages/jenssegers/mongodb) [![Total Downloads](http://img.shields.io/packagist/dm/jenssegers/mongodb.svg)](https://packagist.org/packages/jenssegers/mongodb) [![Build Status](http://img.shields.io/travis/jenssegers/laravel-mongodb.svg)](https://travis-ci.org/jenssegers/laravel-mongodb) [![Coverage Status](http://img.shields.io/coveralls/jenssegers/laravel-mongodb.svg)](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, using the original Laravel API. *This library extends the original Laravel classes, so it uses exactly the same methods.*
### Upgrading from v1 to v2
In this new version, embedded documents are no longer saved to the parent model using an attribute with a leading underscore. If you have a relation like `embedsMany('Book')`, these books are now stored under `$model['books']` instead of `$model['_books']`. This was changed to make embedded relations less confusing for new developers.
If you want to upgrade to this new version without having to change all your existing database objects, you can modify your embedded relations to use a non-default local key including the underscore:
$this->embedsMany('Book', '_books');
Read the full changelog at https://github.com/jenssegers/laravel-mongodb/releases/tag/v2.0.0
Installation Installation
------------ ------------
...@@ -65,7 +75,7 @@ Tell your model to use the MongoDB model and set the collection (alias for table ...@@ -65,7 +75,7 @@ Tell your model to use the MongoDB model and set the collection (alias for table
} }
If you are using a different database driver as the default one, you will need to specify the mongodb connection within your model by changing the `connection` property: If you are using a different database driver as the default one, you will need to specify the mongodb connection name within your model by changing the `connection` property:
use Jenssegers\Mongodb\Model as Eloquent; use Jenssegers\Mongodb\Model as Eloquent;
...@@ -94,15 +104,15 @@ This will allow you to use your registered alias like: ...@@ -94,15 +104,15 @@ This will allow you to use your registered alias like:
Query Builder Query Builder
------------- -------------
The database driver plugs right into the original query builder. When using mongodb connections you will be able to build fluent queries to perform database operations. For your convenience, there is a `collection` alias for `table` as well as some additional mongodb specific operators/operations. The database driver plugs right into the original query builder. When using mongodb connections, you will be able to build fluent queries to perform database operations. For your convenience, there is a `collection` alias for `table` as well as some additional mongodb specific operators/operations.
// With custom connection
$user = DB::connection('mongodb')->collection('users')->get();
// Using default connection
$users = DB::collection('users')->get(); $users = DB::collection('users')->get();
$user = DB::collection('users')->where('name', 'John')->first(); $user = DB::collection('users')->where('name', 'John')->first();
If you did not change your default database connection, you will need to specify it when querying.
$user = DB::connection('mongodb')->collection('users')->get();
Read more about the query builder on http://laravel.com/docs/queries Read more about the query builder on http://laravel.com/docs/queries
Schema Schema
...@@ -170,7 +180,7 @@ Examples ...@@ -170,7 +180,7 @@ Examples
**And Statements** **And Statements**
$users = User::where('votes', '>', 100)->where('name', '=', 'John')->get(); $users = User::where('votes', '>', 100)->where('name', '=', 'John')->get();
**Using Where In With An Array** **Using Where In With An Array**
$users = User::whereIn('age', array(16, 18, 20))->get(); $users = User::whereIn('age', array(16, 18, 20))->get();
...@@ -258,15 +268,15 @@ You may also specify additional columns to update: ...@@ -258,15 +268,15 @@ You may also specify additional columns to update:
When soft deleting a model, it is not actually removed from your database. Instead, a deleted_at timestamp is set on the record. To enable soft deletes for a model, apply the SoftDeletingTrait to the model: When soft deleting a model, it is not actually removed from your database. Instead, a deleted_at timestamp is set on the record. To enable soft deletes for a model, apply the SoftDeletingTrait to the model:
use Jenssegers\Mongodb\Eloquent\SoftDeletingTrait; use Jenssegers\Mongodb\Eloquent\SoftDeletingTrait;
class User extends Eloquent { class User extends Eloquent {
use SoftDeletingTrait; use SoftDeletingTrait;
protected $dates = ['deleted_at']; protected $dates = ['deleted_at'];
} }
For more information check http://laravel.com/docs/eloquent#soft-deleting For more information check http://laravel.com/docs/eloquent#soft-deleting
...@@ -314,7 +324,40 @@ Matches documents that satisfy a JavaScript expression. For more information che ...@@ -314,7 +324,40 @@ Matches documents that satisfy a JavaScript expression. For more information che
### Inserts, updates and deletes ### Inserts, updates and deletes
All basic insert, update, delete and select methods should be implemented. Inserting, updating and deleting records works just like the original Eloquent.
**Saving a new model**
$user = new User;
$user->name = 'John';
$user->save();
You may also use the create method to save a new model in a single line:
User::create(array('name' => 'John'));
**Updating a model**
o update a model, you may retrieve it, change an attribute, and use the save method.
$user = User::first();
$user->email = 'john@foo.com';
$user->save();
*There is also support for upsert operations, check https://github.com/jenssegers/laravel-mongodb#mongodb-specific-operations*
**Deleting a model**
To delete a model, simply call the delete method on the instance:
$user = User::first();
$user->delete();
Or deleting a model by its key:
User::destroy('517c43667db388101e00000f');
For more information about model manipulation, check http://laravel.com/docs/eloquent#insert-update-delete
### Dates ### Dates
...@@ -386,7 +429,7 @@ Other relations are not yet supported, but may be added in the future. Read more ...@@ -386,7 +429,7 @@ Other relations are not yet supported, but may be added in the future. Read more
### EmbedsMany Relations ### EmbedsMany Relations
If you want to embed documents, rather than referencing them, you can use the `embedsMany` relation: If you want to embed models, rather than referencing them, you can use the `embedsMany` relation. This relation is similar to the `hasMany` relation, but embeds the models inside the parent object.
use Jenssegers\Mongodb\Model as Eloquent; use Jenssegers\Mongodb\Model as Eloquent;
...@@ -399,52 +442,71 @@ If you want to embed documents, rather than referencing them, you can use the `e ...@@ -399,52 +442,71 @@ If you want to embed documents, rather than referencing them, you can use the `e
} }
Now we can access the user's books through the dynamic property: You access the embedded models through the dynamic property:
$books = User::first()->books; $books = User::first()->books;
When using embedded documents, there will also be an inverse relation available: The inverse relation is auto*magically* available, you don't need to define this reverse relation.
$user = $book->user; $user = $book->user;
Inserting and updating embedded documents works just like the `belongsTo` relation: Inserting and updating embedded models works similar to the `hasMany` relation:
$book = new Book(array('title' => 'A Game of Thrones')); $book = new Book(array('title' => 'A Game of Thrones'));
$user = User::first(); $user = User::first();
$book = $user->books()->save($book); $book = $user->books()->save($book);
// or
$book = $user->books()->create(array('title' => 'A Game of Thrones'))
You can remove an embedded document by using the `destroy()` method: You can update embedded models using their `save` method (available since release 2.0.0):
$book = $user->books()->first(); $book = $user->books()->first();
$user->books()->destroy($book->_id); $book->title = 'A Game of Thrones';
$book->save();
You can remove an embedded model by using the `destroy` method on the relation, or the `delete` method on the model (available since release 2.0.0):
$book = $user->books()->first();
$book->delete();
// 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: If you want to add or remove an embedded model, without touching the database, you can use the `associate` and `dissociate` methods. To eventually write the changes to the database, save the parent object:
$user->books()->associate($book); $user->books()->associate($book);
$user->save(); $user->save();
Again, you may override the conventional local key by passing a second argument to the embedsMany method: Like other relations, embedsMany assumes the local key of the relationship based on the model name. You can override the default local key by passing a second argument to the embedsMany method:
return $this->embedsMany('Book', 'local_key'); return $this->embedsMany('Book', 'local_key');
When using embedded documents, they will be stored in a _relation attribute of the parent document. This attribute is hidden by default when using `toArray` or `toJson`. If you want the attribute to be exposed, add it to `$exposed` property definition to your model: Embedded relations will return a Collection of embedded items instead of a query builder. To allow a more query-like behavior, embedded relations will return a modified version of the Collection class with support for the following **additional** operations:
use Jenssegers\Mongodb\Model as Eloquent; - where($key, $operator, $value)
- whereIn($key, $values) and whereNotIn($key, $values)
- whereBetween($key, $values) and whereNotBetween($key, $values)
- whereNull($key) and whereNotNull($key)
- orderBy($key, $direction)
- oldest() and latest()
- limit($value)
- offset($value)
- skip($value)
class User extends Eloquent { This allows you to execute simple queries on the collection results:
protected $exposed = array('_books'); $books = $user->books()->where('rating', '>', 5)->orderBy('title')->get();
} **Note:** Because embedded models are not stored in a separate collection, you can not query all of embedded models. You will always have to access them through the parent model.
### EmbedsOne Relations ### EmbedsOne Relations
There is also an EmbedsOne relation, which works similar to the EmbedsMany relation, but only stores one embedded model. The embedsOne relation is similar to the EmbedsMany relation, but only embeds a single model.
use Jenssegers\Mongodb\Model as Eloquent; use Jenssegers\Mongodb\Model as Eloquent;
...@@ -457,17 +519,31 @@ There is also an EmbedsOne relation, which works similar to the EmbedsMany relat ...@@ -457,17 +519,31 @@ There is also an EmbedsOne relation, which works similar to the EmbedsMany relat
} }
Now we can access the book's author through the dynamic property: You access the embedded models through the dynamic property:
$author = Book::first()->author; $author = Book::first()->author;
Inserting and updating embedded documents works just like the `embedsMany` relation: Inserting and updating embedded models works similar to the `hasOne` relation:
$author = new Author(array('name' => 'John Doe')); $author = new Author(array('name' => 'John Doe'));
$book = Books::first(); $book = Books::first();
$author = $user->author()->save($author); $author = $book->author()->save($author);
// or
$author = $book->author()->create(array('name' => 'John Doe'));
You can update the embedded model using the `save` method (available since release 2.0.0):
$author = $book->author;
$author->name = 'Jane Doe';
$author->save();
You can replace the embedded model with a new model like this:
$newAuthor = new Author(array('name' => 'Jane Doe'));
$book->author()->save($newAuthor);
### MySQL Relations ### MySQL Relations
...@@ -527,13 +603,19 @@ Optional: if you don't pass a closure to the raw method, the internal MongoColle ...@@ -527,13 +603,19 @@ Optional: if you don't pass a closure to the raw method, the internal MongoColle
$model = User::raw()->findOne(array('age' => array('$lt' => 18))); $model = User::raw()->findOne(array('age' => array('$lt' => 18)));
The MongoClient and MongoDB objects can be accessed like this: The internal MongoClient and MongoDB objects can be accessed like this:
$client = DB::getMongoClient(); $client = DB::getMongoClient();
$db = DB::getMongoDB(); $db = DB::getMongoDB();
### MongoDB specific operations ### MongoDB specific operations
**Cursor timeout**
To prevent MongoCursorTimeout exceptions, you can manually set a timeout value that will be applied to the cursor:
DB::collection('users')->timeout(-1)->get();
**Upsert** **Upsert**
Update or insert a document. Additional options for the update method are passed directly to the native update method. Update or insert a document. Additional options for the update method are passed directly to the native update method.
...@@ -541,6 +623,12 @@ Update or insert a document. Additional options for the update method are passed ...@@ -541,6 +623,12 @@ Update or insert a document. Additional options for the update method are passed
DB::collection('users')->where('name', 'John') DB::collection('users')->where('name', 'John')
->update($data, array('upsert' => true)); ->update($data, array('upsert' => true));
**Projections**
You can apply projections to your queries using the `project` method.
DB::collection('items')->project(array('tags' => array('$slice' => 1)))->get();
**Push** **Push**
Add an items to an array. Add an items to an array.
......
<?php namespace Jenssegers\Mongodb\Auth; <?php namespace Jenssegers\Mongodb\Auth;
use DateTime;
use MongoDate;
class DatabaseReminderRepository extends \Illuminate\Auth\Reminders\DatabaseReminderRepository { class DatabaseReminderRepository extends \Illuminate\Auth\Reminders\DatabaseReminderRepository {
/**
* Build the record payload for the table.
*
* @param string $email
* @param string $token
* @return array
*/
protected function getPayload($email, $token)
{
return array('email' => $email, 'token' => $token, 'created_at' => new MongoDate);
}
/** /**
* Determine if the reminder has expired. * Determine if the reminder has expired.
* *
...@@ -10,16 +25,22 @@ class DatabaseReminderRepository extends \Illuminate\Auth\Reminders\DatabaseRemi ...@@ -10,16 +25,22 @@ class DatabaseReminderRepository extends \Illuminate\Auth\Reminders\DatabaseRemi
*/ */
protected function reminderExpired($reminder) protected function reminderExpired($reminder)
{ {
// Convert to array so that we can pass it to the parent method // Convert MongoDate to a date string.
if (is_object($reminder)) if ($reminder['created_at'] instanceof MongoDate)
{ {
$reminder = (array) $reminder; $date = new DateTime;
$date->setTimestamp($reminder['created_at']->sec);
$reminder['created_at'] = $date->format('Y-m-d H:i:s');
} }
// Convert the DateTime object that got saved to MongoDB // Convert DateTime to a date string (backwards compatibility).
if (is_array($reminder['created_at'])) elseif (is_array($reminder['created_at']))
{ {
$reminder['created_at'] = $reminder['created_at']['date'] + $reminder['created_at']['timezone']; $date = DateTime::__set_state($reminder['created_at']);
$reminder['created_at'] = $date->format('Y-m-d H:i:s');
} }
return parent::reminderExpired($reminder); return parent::reminderExpired($reminder);
......
...@@ -26,6 +26,7 @@ class Collection { ...@@ -26,6 +26,7 @@ class Collection {
public function __construct(Connection $connection, MongoCollection $collection) public function __construct(Connection $connection, MongoCollection $collection)
{ {
$this->connection = $connection; $this->connection = $connection;
$this->collection = $collection; $this->collection = $collection;
} }
...@@ -38,29 +39,34 @@ class Collection { ...@@ -38,29 +39,34 @@ class Collection {
*/ */
public function __call($method, $parameters) public function __call($method, $parameters)
{ {
$query = array();
// Build the query string. // Build the query string.
$query = $parameters; foreach ($parameters as $parameter)
foreach ($query as &$param)
{ {
try try
{ {
$param = json_encode($param); $query[] = json_encode($parameter);
} }
catch (Exception $e) catch (Exception $e)
{ {
$param = '{...}'; $query[] = '{...}';
} }
} }
$start = microtime(true); $start = microtime(true);
// Execute the query.
$result = call_user_func_array(array($this->collection, $method), $parameters); $result = call_user_func_array(array($this->collection, $method), $parameters);
// Log the query. // Once we have run the query we will calculate the time that it took to run and
$this->connection->logQuery( // then log the query, bindings, and execution time so we will report them on
$this->collection->getName() . '.' . $method . '(' . join(',', $query) . ')', // the event that the developer needs them. We'll log time in milliseconds.
array(), $this->connection->getElapsedTime($start)); $time = $this->connection->getElapsedTime($start);
// Convert the query to a readable string.
$queryString = $this->collection->getName() . '.' . $method . '(' . join(',', $query) . ')';
$this->connection->logQuery($queryString, array(), $time);
return $result; return $result;
} }
......
...@@ -16,6 +16,146 @@ class Builder extends EloquentBuilder { ...@@ -16,6 +16,146 @@ class Builder extends EloquentBuilder {
'count', 'min', 'max', 'avg', 'sum', 'exists', 'push', 'pull' 'count', 'min', 'max', 'avg', 'sum', 'exists', 'push', 'pull'
); );
/**
* Update a record in the database.
*
* @param array $values
* @return int
*/
public function update(array $values)
{
// Intercept operations on embedded models and delegate logic
// to the parent relation instance.
if ($relation = $this->model->getParentRelation())
{
$relation->performUpdate($this->model, $values);
return 1;
}
return parent::update($values);
}
/**
* Insert a new record into the database.
*
* @param array $values
* @return bool
*/
public function insert(array $values)
{
// Intercept operations on embedded models and delegate logic
// to the parent relation instance.
if ($relation = $this->model->getParentRelation())
{
$relation->performInsert($this->model, $values);
return true;
}
return parent::insert($values);
}
/**
* Insert a new record and get the value of the primary key.
*
* @param array $values
* @param string $sequence
* @return int
*/
public function insertGetId(array $values, $sequence = null)
{
// Intercept operations on embedded models and delegate logic
// to the parent relation instance.
if ($relation = $this->model->getParentRelation())
{
$relation->performInsert($this->model, $values);
return $this->model->getKey();
}
return parent::insertGetId($values, $sequence);
}
/**
* Delete a record from the database.
*
* @return mixed
*/
public function delete()
{
// Intercept operations on embedded models and delegate logic
// to the parent relation instance.
if ($relation = $this->model->getParentRelation())
{
$relation->performDelete($this->model);
return $this->model->getKey();
}
return parent::delete();
}
/**
* Increment a column's value by a given amount.
*
* @param string $column
* @param int $amount
* @param array $extra
* @return int
*/
public function increment($column, $amount = 1, array $extra = array())
{
// Intercept operations on embedded models and delegate logic
// to the parent relation instance.
if ($relation = $this->model->getParentRelation())
{
$value = $this->model->{$column};
// When doing increment and decrements, Eloquent will automatically
// sync the original attributes. We need to change the attribute
// temporary in order to trigger an update query.
$this->model->{$column} = null;
$this->model->syncOriginalAttribute($column);
$result = $this->model->update(array($column => $value));
return $result;
}
return parent::increment($column, $amount, $extra);
}
/**
* Decrement a column's value by a given amount.
*
* @param string $column
* @param int $amount
* @param array $extra
* @return int
*/
public function decrement($column, $amount = 1, array $extra = array())
{
// Intercept operations on embedded models and delegate logic
// to the parent relation instance.
if ($relation = $this->model->getParentRelation())
{
$value = $this->model->{$column};
// When doing increment and decrements, Eloquent will automatically
// sync the original attributes. We need to change the attribute
// temporary in order to trigger an update query.
$this->model->{$column} = null;
$this->model->syncOriginalAttribute($column);
return $this->model->update(array($column => $value));
}
return parent::decrement($column, $amount, $extra);
}
/** /**
* Add the "has" condition where clause to the query. * Add the "has" condition where clause to the query.
* *
...@@ -77,26 +217,12 @@ class Builder extends EloquentBuilder { ...@@ -77,26 +217,12 @@ class Builder extends EloquentBuilder {
// Get raw results from the query builder. // Get raw results from the query builder.
$results = $this->query->raw($expression); $results = $this->query->raw($expression);
$connection = $this->model->getConnectionName();
// Convert MongoCursor results to a collection of models. // Convert MongoCursor results to a collection of models.
if ($results instanceof MongoCursor) if ($results instanceof MongoCursor)
{ {
$results = iterator_to_array($results, false); $results = iterator_to_array($results, false);
$models = array(); return $this->model->hydrate($results);
// 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. // The result is a single object.
...@@ -104,7 +230,7 @@ class Builder extends EloquentBuilder { ...@@ -104,7 +230,7 @@ class Builder extends EloquentBuilder {
{ {
$model = $this->model->newFromBuilder($results); $model = $this->model->newFromBuilder($results);
$model->setConnection($connection); $model->setConnection($this->model->getConnection());
return $model; return $model;
} }
......
<?php namespace Jenssegers\Mongodb\Eloquent;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
class Collection extends EloquentCollection {
/**
* Simulate a get clause on the collection.
*
* @param mixed $key
* @param mixed $default
* @return mixed
*/
public function get($key = null, $default = null)
{
if (is_null($key) and is_null($default))
{
return $this;
}
return parent::get($key, $default);
}
/**
* Simulate a basic where clause on the collection.
*
* @param string $key
* @param string $operator
* @param mixed $value
* @param string $boolean
* @return $this
*/
public function where($key, $operator = null, $value = null)
{
// Here we will make some assumptions about the operator. If only 2 values are
// passed to the method, we will assume that the operator is an equals sign
// and keep going.
if (func_num_args() == 2)
{
list($value, $operator) = array($operator, '=');
}
return $this->filter(function($item) use ($key, $operator, $value)
{
$actual = $item->{$key};
switch ($operator)
{
case '<>':
case '!=':
return $actual != $value;
break;
case '>':
return $actual > $value;
break;
case '<':
return $actual < $value;
break;
case '>=':
return $actual >= $value;
break;
case '<=':
return $actual <= $value;
break;
case 'between':
return $actual >= $value[0] and $actual <= $value[1];
break;
case 'not between':
return $actual < $value[0] or $actual > $value[1];
break;
case 'in':
return in_array($actual, $value);
break;
case 'not in':
return ! in_array($actual, $value);
break;
case '=':
default:
return $actual == $value;
break;
}
});
}
/**
* Add a where between statement to the query.
*
* @param string $column
* @param array $values
* @param string $boolean
* @param bool $not
* @return $this
*/
public function whereBetween($column, array $values, $boolean = 'and', $not = false)
{
$type = $not ? 'not between' : 'between';
return $this->where($column, $type, $values);
}
/**
* Add a where not between statement to the query.
*
* @param string $column
* @param array $values
* @param string $boolean
* @return $this
*/
public function whereNotBetween($column, array $values, $boolean = 'and')
{
return $this->whereBetween($column, $values, $boolean, true);
}
/**
* Add a "where in" clause to the query.
*
* @param string $column
* @param mixed $values
* @param string $boolean
* @param bool $not
* @return $this
*/
public function whereIn($column, $values, $boolean = 'and', $not = false)
{
$type = $not ? 'not in' : 'in';
return $this->where($column, $type, $values);
}
/**
* Add a "where not in" clause to the query.
*
* @param string $column
* @param mixed $values
* @param string $boolean
* @return $this
*/
public function whereNotIn($column, $values, $boolean = 'and')
{
return $this->whereIn($column, $values, $boolean, true);
}
/**
* Add a "where null" clause to the query.
*
* @param string $column
* @param string $boolean
* @param bool $not
* @return $this
*/
public function whereNull($column, $boolean = 'and', $not = false)
{
return $this->where($column, '=', null);
}
/**
* Add a "where not null" clause to the query.
*
* @param string $column
* @param string $boolean
* @return $this
*/
public function whereNotNull($column, $boolean = 'and')
{
return $this->where($column, '!=', null);
}
/**
* Simulate order by clause on the collection.
*
* @param string $key
* @param string $direction
* @return $this
*/
public function orderBy($key, $direction = 'asc')
{
$descending = strtolower($direction) == 'desc';
return $this->sortBy($key, SORT_REGULAR, $descending);
}
/**
* Add an "order by" clause for a timestamp to the query.
*
* @param string $column
* @return $this
*/
public function latest($column = 'created_at')
{
return $this->orderBy($column, 'desc');
}
/**
* Add an "order by" clause for a timestamp to the query.
*
* @param string $column
* @return $this
*/
public function oldest($column = 'created_at')
{
return $this->orderBy($column, 'asc');
}
/**
* Set the "offset" value of the query.
*
* @param int $value
* @return $this
*/
public function offset($value)
{
$offset = max(0, $value);
return $this->slice($offset);
}
/**
* Alias to set the "offset" value of the query.
*
* @param int $value
* @return $this
*/
public function skip($value)
{
return $this->offset($value);
}
/**
* Set the "limit" value of the query.
*
* @param int $value
* @return $this
*/
public function limit($value)
{
return $this->take($value);
}
}
...@@ -4,6 +4,8 @@ use Illuminate\Database\Eloquent\Collection; ...@@ -4,6 +4,8 @@ use Illuminate\Database\Eloquent\Collection;
use Jenssegers\Mongodb\DatabaseManager as Resolver; use Jenssegers\Mongodb\DatabaseManager as Resolver;
use Jenssegers\Mongodb\Eloquent\Builder; use Jenssegers\Mongodb\Eloquent\Builder;
use Jenssegers\Mongodb\Query\Builder as QueryBuilder; use Jenssegers\Mongodb\Query\Builder as QueryBuilder;
use Illuminate\Database\Eloquent\Relations\Relation;
use Jenssegers\Mongodb\Relations\EmbedsOneOrMany;
use Jenssegers\Mongodb\Relations\EmbedsMany; use Jenssegers\Mongodb\Relations\EmbedsMany;
use Jenssegers\Mongodb\Relations\EmbedsOne; use Jenssegers\Mongodb\Relations\EmbedsOne;
...@@ -29,11 +31,11 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -29,11 +31,11 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
protected $primaryKey = '_id'; protected $primaryKey = '_id';
/** /**
* The attributes that should be exposed for toArray and toJson. * The parent relation instance.
* *
* @var array * @var Relation
*/ */
protected $exposed = array(); protected $parentRelation;
/** /**
* The connection resolver instance. * The connection resolver instance.
...@@ -88,7 +90,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -88,7 +90,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
if (is_null($localKey)) if (is_null($localKey))
{ {
$localKey = '_' . $relation; $localKey = $relation;
} }
if (is_null($foreignKey)) if (is_null($foreignKey))
...@@ -124,7 +126,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -124,7 +126,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
if (is_null($localKey)) if (is_null($localKey))
{ {
$localKey = '_' . $relation; $localKey = $relation;
} }
if (is_null($foreignKey)) if (is_null($foreignKey))
...@@ -220,7 +222,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -220,7 +222,7 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
public function getAttribute($key) public function getAttribute($key)
{ {
// Check if the key is an array dot notation. // Check if the key is an array dot notation.
if (strpos($key, '.') !== false) if (str_contains($key, '.'))
{ {
$attributes = array_dot($this->attributes); $attributes = array_dot($this->attributes);
...@@ -230,6 +232,32 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -230,6 +232,32 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
} }
} }
$camelKey = camel_case($key);
// If the "attribute" exists as a method on the model, it may be an
// embedded model. If so, we need to return the result before it
// is handled by the parent method.
if (method_exists($this, $camelKey))
{
$relations = $this->$camelKey();
// This attribute matches an embedsOne or embedsMany relation so we need
// to return the relation results instead of the interal attributes.
if ($relations instanceof EmbedsOneOrMany)
{
// If the key already exists in the relationships array, it just means the
// relationship has already been loaded, so we'll just return it out of
// here because there is no need to query within the relations twice.
if (array_key_exists($key, $this->relations))
{
return $this->relations[$key];
}
// Get the relation results.
return $this->getRelationshipFromMethod($key, $camelKey);
}
}
return parent::getAttribute($key); return parent::getAttribute($key);
} }
...@@ -241,12 +269,8 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -241,12 +269,8 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
*/ */
protected function getAttributeFromArray($key) protected function getAttributeFromArray($key)
{ {
if (array_key_exists($key, $this->attributes)) // Support keys in dot notation.
{ if (str_contains($key, '.'))
return $this->attributes[$key];
}
else if (strpos($key, '.') !== false)
{ {
$attributes = array_dot($this->attributes); $attributes = array_dot($this->attributes);
...@@ -255,6 +279,8 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -255,6 +279,8 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
return $attributes[$key]; return $attributes[$key];
} }
} }
return parent::getAttributeFromArray($key);
} }
/** /**
...@@ -270,9 +296,21 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -270,9 +296,21 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
if ($key == '_id' and is_string($value)) if ($key == '_id' and is_string($value))
{ {
$builder = $this->newBaseQueryBuilder(); $builder = $this->newBaseQueryBuilder();
$value = $builder->convertKey($value); $value = $builder->convertKey($value);
} }
// Support keys in dot notation.
elseif (str_contains($key, '.'))
{
if (in_array($key, $this->getDates()) && $value)
{
$value = $this->fromDateTime($value);
}
array_set($this->attributes, $key, $value); return;
}
parent::setAttribute($key, $value); parent::setAttribute($key, $value);
} }
...@@ -295,22 +333,6 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -295,22 +333,6 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
{ {
$value = (string) $value; $value = (string) $value;
} }
// If the attribute starts with an underscore, it might be the
// internal array of embedded documents. In that case, we need
// to hide these from the output so that the relation-based
// attribute can take over.
else if (starts_with($key, '_') and ! in_array($key, $this->exposed))
{
$camelKey = camel_case($key);
// If we can find a method that responds to this relation we
// will remove it from the output.
if (method_exists($this, $camelKey))
{
unset($attributes[$key]);
}
}
} }
return $attributes; return $attributes;
...@@ -345,9 +367,25 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -345,9 +367,25 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
{ {
if ($parameters = func_get_args()) if ($parameters = func_get_args())
{ {
$unique = false;
if (count($parameters) == 3)
{
list($column, $values, $unique) = $parameters;
}
else
{
list($column, $values) = $parameters;
}
// Do batch push by default.
if ( ! is_array($values)) $values = array($values);
$query = $this->setKeysForSaveQuery($this->newQuery()); $query = $this->setKeysForSaveQuery($this->newQuery());
return call_user_func_array(array($query, 'push'), $parameters); $this->pushAttributeValues($column, $values, $unique);
return $query->push($column, $values, $unique);
} }
return parent::push(); return parent::push();
...@@ -358,22 +396,87 @@ abstract class Model extends \Jenssegers\Eloquent\Model { ...@@ -358,22 +396,87 @@ abstract class Model extends \Jenssegers\Eloquent\Model {
* *
* @return mixed * @return mixed
*/ */
public function pull() public function pull($column, $values)
{ {
// Do batch pull by default.
if ( ! is_array($values)) $values = array($values);
$query = $this->setKeysForSaveQuery($this->newQuery()); $query = $this->setKeysForSaveQuery($this->newQuery());
return call_user_func_array(array($query, 'pull'), func_get_args()); $this->pullAttributeValues($column, $values);
return $query->pull($column, $values);
}
/**
* Append one or more values to the underlying attribute value and sync with original.
*
* @param string $column
* @param array $values
* @param bool $unique
* @return void
*/
protected function pushAttributeValues($column, array $values, $unique = false)
{
$current = $this->getAttributeFromArray($column) ?: array();
foreach ($values as $value)
{
// Don't add duplicate values when we only want unique values.
if ($unique and in_array($value, $current)) continue;
array_push($current, $value);
}
$this->attributes[$column] = $current;
$this->syncOriginalAttribute($column);
} }
/** /**
* Set the exposed attributes for the model. * Rempove one or more values to the underlying attribute value and sync with original.
* *
* @param array $exposed * @param string $column
* @param array $values
* @return void * @return void
*/ */
public function setExposed(array $exposed) protected function pullAttributeValues($column, array $values)
{
$current = $this->getAttributeFromArray($column) ?: array();
foreach ($values as $value)
{
$keys = array_keys($current, $value);
foreach ($keys as $key)
{
unset($current[$key]);
}
}
$this->attributes[$column] = array_values($current);
$this->syncOriginalAttribute($column);
}
/**
* Set the parent relation.
*
* @param Relation $relation
*/
public function setParentRelation(Relation $relation)
{
$this->parentRelation = $relation;
}
/**
* Get the parent relation.
*
* @return Relation
*/
public function getParentRelation()
{ {
$this->exposed = $exposed; return $this->parentRelation;
} }
/** /**
......
...@@ -6,10 +6,11 @@ use MongoDate; ...@@ -6,10 +6,11 @@ use MongoDate;
use DateTime; use DateTime;
use Closure; use Closure;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Query\Expression; use Illuminate\Database\Query\Expression;
use Jenssegers\Mongodb\Connection; use Jenssegers\Mongodb\Connection;
class Builder extends \Illuminate\Database\Query\Builder { class Builder extends QueryBuilder {
/** /**
* The database collection * The database collection
...@@ -18,6 +19,20 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -18,6 +19,20 @@ class Builder extends \Illuminate\Database\Query\Builder {
*/ */
protected $collection; protected $collection;
/**
* The column projections.
*
* @var array
*/
public $projections;
/**
* The cursor timeout value.
*
* @var int
*/
public $timeout;
/** /**
* All of the available clause operators. * All of the available clause operators.
* *
...@@ -59,6 +74,32 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -59,6 +74,32 @@ class Builder extends \Illuminate\Database\Query\Builder {
$this->connection = $connection; $this->connection = $connection;
} }
/**
* Set the projections.
*
* @param array $columns
* @return $this
*/
public function project($columns)
{
$this->projections = is_array($columns) ? $columns : func_get_args();
return $this;
}
/**
* Set the cursor timeout in seconds.
*
* @param int $seconds
* @return $this
*/
public function timeout($seconds)
{
$this->timeout = $seconds;
return $this;
}
/** /**
* Execute a query for a single record by ID. * Execute a query for a single record by ID.
* *
...@@ -71,6 +112,17 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -71,6 +112,17 @@ class Builder extends \Illuminate\Database\Query\Builder {
return $this->where('_id', '=', $this->convertKey($id))->first($columns); return $this->where('_id', '=', $this->convertKey($id))->first($columns);
} }
/**
* Execute the query as a "select" statement.
*
* @param array $columns
* @return array|static[]
*/
public function get($columns = array())
{
return parent::get($columns);
}
/** /**
* Execute the query as a fresh "select" statement. * Execute the query as a fresh "select" statement.
* *
...@@ -142,6 +194,7 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -142,6 +194,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
foreach ($this->columns as $column) foreach ($this->columns as $column)
{ {
$key = str_replace('.', '_', $column); $key = str_replace('.', '_', $column);
$group[$key] = array('$last' => '$' . $column); $group[$key] = array('$last' => '$' . $column);
} }
} }
...@@ -152,9 +205,10 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -152,9 +205,10 @@ class Builder extends \Illuminate\Database\Query\Builder {
$pipeline[] = array('$group' => $group); $pipeline[] = array('$group' => $group);
// Apply order and limit // Apply order and limit
if ($this->orders) $pipeline[] = array('$sort' => $this->orders); if ($this->orders) $pipeline[] = array('$sort' => $this->orders);
if ($this->offset) $pipeline[] = array('$skip' => $this->offset); if ($this->offset) $pipeline[] = array('$skip' => $this->offset);
if ($this->limit) $pipeline[] = array('$limit' => $this->limit); if ($this->limit) $pipeline[] = array('$limit' => $this->limit);
if ($this->projections) $pipeline[] = array('$project' => $this->projections);
// Execute aggregation // Execute aggregation
$results = $this->collection->aggregate($pipeline); $results = $this->collection->aggregate($pipeline);
...@@ -179,18 +233,27 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -179,18 +233,27 @@ class Builder extends \Illuminate\Database\Query\Builder {
else else
{ {
$columns = array(); $columns = array();
// Convert select columns to simple projections.
foreach ($this->columns as $column) foreach ($this->columns as $column)
{ {
$columns[$column] = true; $columns[$column] = true;
} }
// Add custom projections.
if ($this->projections)
{
$columns = array_merge($columns, $this->projections);
}
// Execute query and get MongoCursor // Execute query and get MongoCursor
$cursor = $this->collection->find($wheres, $columns); $cursor = $this->collection->find($wheres, $columns);
// Apply order, offset and limit // Apply order, offset and limit
if ($this->orders) $cursor->sort($this->orders); if ($this->timeout) $cursor->timeout($this->timeout);
if ($this->offset) $cursor->skip($this->offset); if ($this->orders) $cursor->sort($this->orders);
if ($this->limit) $cursor->limit($this->limit); if ($this->offset) $cursor->skip($this->offset);
if ($this->limit) $cursor->limit($this->limit);
// Return results as an array with numeric keys // Return results as an array with numeric keys
return iterator_to_array($cursor, false); return iterator_to_array($cursor, false);
...@@ -313,17 +376,18 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -313,17 +376,18 @@ class Builder extends \Illuminate\Database\Query\Builder {
// Since every insert gets treated like a batch insert, we will have to detect // Since every insert gets treated like a batch insert, we will have to detect
// if the user is inserting a single document or an array of documents. // if the user is inserting a single document or an array of documents.
$batch = true; $batch = true;
foreach ($values as $value) foreach ($values as $value)
{ {
// As soon as we find a value that is not an array we assume the user is // As soon as we find a value that is not an array we assume the user is
// inserting a single document. // inserting a single document.
if (!is_array($value)) if ( ! is_array($value))
{ {
$batch = false; break; $batch = false; break;
} }
} }
if (!$batch) $values = array($values); if ( ! $batch) $values = array($values);
// Batch insert // Batch insert
$result = $this->collection->batchInsert($values); $result = $this->collection->batchInsert($values);
...@@ -344,7 +408,7 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -344,7 +408,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
if (1 == (int) $result['ok']) if (1 == (int) $result['ok'])
{ {
if (!$sequence) if (is_null($sequence))
{ {
$sequence = '_id'; $sequence = '_id';
} }
...@@ -363,7 +427,13 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -363,7 +427,13 @@ class Builder extends \Illuminate\Database\Query\Builder {
*/ */
public function update(array $values, array $options = array()) public function update(array $values, array $options = array())
{ {
return $this->performUpdate(array('$set' => $values), $options); // Use $set as default operator.
if ( ! starts_with(key($values), '$'))
{
$values = array('$set' => $values);
}
return $this->performUpdate($values, $options);
} }
/** /**
...@@ -376,11 +446,9 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -376,11 +446,9 @@ class Builder extends \Illuminate\Database\Query\Builder {
*/ */
public function increment($column, $amount = 1, array $extra = array(), array $options = array()) public function increment($column, $amount = 1, array $extra = array(), array $options = array())
{ {
$query = array( $query = array('$inc' => array($column => $amount));
'$inc' => array($column => $amount)
);
if (!empty($extra)) if ( ! empty($extra))
{ {
$query['$set'] = $extra; $query['$set'] = $extra;
} }
...@@ -389,6 +457,7 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -389,6 +457,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
$this->where(function($query) use ($column) $this->where(function($query) use ($column)
{ {
$query->where($column, 'exists', false); $query->where($column, 'exists', false);
$query->orWhereNotNull($column); $query->orWhereNotNull($column);
}); });
...@@ -437,6 +506,7 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -437,6 +506,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
public function delete($id = null) public function delete($id = null)
{ {
$wheres = $this->compileWheres(); $wheres = $this->compileWheres();
$result = $this->collection->remove($wheres); $result = $this->collection->remove($wheres);
if (1 == (int) $result['ok']) if (1 == (int) $result['ok'])
...@@ -490,7 +560,7 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -490,7 +560,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
} }
// Create an expression for the given value // Create an expression for the given value
else if (!is_null($expression)) else if ( ! is_null($expression))
{ {
return new Expression($expression); return new Expression($expression);
} }
...@@ -511,10 +581,17 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -511,10 +581,17 @@ class Builder extends \Illuminate\Database\Query\Builder {
// Use the addToSet operator in case we only want unique items. // Use the addToSet operator in case we only want unique items.
$operator = $unique ? '$addToSet' : '$push'; $operator = $unique ? '$addToSet' : '$push';
// Check if we are pushing multiple values.
$batch = (is_array($value) and array_keys($value) === range(0, count($value) - 1));
if (is_array($column)) if (is_array($column))
{ {
$query = array($operator => $column); $query = array($operator => $column);
} }
else if ($batch)
{
$query = array($operator => array($column => array('$each' => $value)));
}
else else
{ {
$query = array($operator => array($column => $value)); $query = array($operator => array($column => $value));
...@@ -532,13 +609,19 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -532,13 +609,19 @@ class Builder extends \Illuminate\Database\Query\Builder {
*/ */
public function pull($column, $value = null) public function pull($column, $value = null)
{ {
// Check if we passed an associative array.
$batch = (is_array($value) and array_keys($value) === range(0, count($value) - 1));
// If we are pulling multiple values, we need to use $pullAll.
$operator = $batch ? '$pullAll' : '$pull';
if (is_array($column)) if (is_array($column))
{ {
$query = array('$pull' => $column); $query = array($operator => $column);
} }
else else
{ {
$query = array('$pull' => array($column => $value)); $query = array($operator => array($column => $value));
} }
return $this->performUpdate($query); return $this->performUpdate($query);
...@@ -552,7 +635,7 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -552,7 +635,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
*/ */
public function drop($columns) public function drop($columns)
{ {
if (!is_array($columns)) $columns = array($columns); if ( ! is_array($columns)) $columns = array($columns);
$fields = array(); $fields = array();
...@@ -585,13 +668,14 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -585,13 +668,14 @@ class Builder extends \Illuminate\Database\Query\Builder {
*/ */
protected function performUpdate($query, array $options = array()) protected function performUpdate($query, array $options = array())
{ {
// Default options // Update multiple items by default.
$default = array('multiple' => true); if ( ! array_key_exists('multiple', $options))
{
// Merge options and override default options $options['multiple'] = true;
$options = array_merge($default, $options); }
$wheres = $this->compileWheres(); $wheres = $this->compileWheres();
$result = $this->collection->update($wheres, $query, $options); $result = $this->collection->update($wheres, $query, $options);
if (1 == (int) $result['ok']) if (1 == (int) $result['ok'])
...@@ -682,7 +766,7 @@ class Builder extends \Illuminate\Database\Query\Builder { ...@@ -682,7 +766,7 @@ class Builder extends \Illuminate\Database\Query\Builder {
} }
// Convert id's. // Convert id's.
if (isset($where['column']) and $where['column'] == '_id') if (isset($where['column']) and ($where['column'] == '_id' or ends_with($where['column'], '._id')))
{ {
// Multiple values. // Multiple values.
if (isset($where['values'])) if (isset($where['values']))
......
<?php namespace Jenssegers\Mongodb\Relations; <?php namespace Jenssegers\Mongodb\Relations;
use Jenssegers\Mongodb\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\BelongsToMany as EloquentBelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany as EloquentBelongsToMany;
...@@ -40,15 +40,58 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -40,15 +40,58 @@ class BelongsToMany extends EloquentBelongsToMany {
} }
} }
/**
* Save a new model and attach it to the parent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param array $joining
* @param bool $touch
* @return \Illuminate\Database\Eloquent\Model
*/
public function save(Model $model, array $joining = array(), $touch = true)
{
$model->save(array('touch' => false));
$this->attach($model, $joining, $touch);
return $model;
}
/**
* Create a new instance of the related model.
*
* @param array $attributes
* @param array $joining
* @param bool $touch
* @return \Illuminate\Database\Eloquent\Model
*/
public function create(array $attributes, array $joining = array(), $touch = true)
{
$instance = $this->related->newInstance($attributes);
// Once we save the related model, we need to attach it to the base model via
// through intermediate table so we'll use the existing "attach" method to
// accomplish this which will insert the record and any more attributes.
$instance->save(array('touch' => false));
$this->attach($instance, $joining, $touch);
return $instance;
}
/** /**
* Sync the intermediate tables with a list of IDs or collection of models. * Sync the intermediate tables with a list of IDs or collection of models.
* *
* @param array $ids * @param mixed $ids
* @param bool $detaching * @param bool $detaching
* @return void * @return array
*/ */
public function sync($ids, $detaching = true) public function sync($ids, $detaching = true)
{ {
$changes = array(
'attached' => array(), 'detached' => array(), 'updated' => array()
);
if ($ids instanceof Collection) $ids = $ids->modelKeys(); if ($ids instanceof Collection) $ids = $ids->modelKeys();
// First we need to attach any of the associated models that are not currently // First we need to attach any of the associated models that are not currently
...@@ -56,6 +99,9 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -56,6 +99,9 @@ class BelongsToMany extends EloquentBelongsToMany {
// if they exist in the array of current ones, and if not we will insert. // if they exist in the array of current ones, and if not we will insert.
$current = $this->parent->{$this->otherKey} ?: array(); $current = $this->parent->{$this->otherKey} ?: array();
// See issue #256.
if ($current instanceof Collection) $current = $ids->modelKeys();
$records = $this->formatSyncList($ids); $records = $this->formatSyncList($ids);
$detach = array_diff($current, array_keys($records)); $detach = array_diff($current, array_keys($records));
...@@ -66,36 +112,36 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -66,36 +112,36 @@ class BelongsToMany extends EloquentBelongsToMany {
if ($detaching and count($detach) > 0) if ($detaching and count($detach) > 0)
{ {
$this->detach($detach); $this->detach($detach);
$changes['detached'] = (array) array_map('intval', $detach);
} }
// Now we are finally ready to attach the new records. Note that we'll disable // Now we are finally ready to attach the new records. Note that we'll disable
// touching until after the entire operation is complete so we don't fire a // touching until after the entire operation is complete so we don't fire a
// ton of touch operations until we are totally done syncing the records. // ton of touch operations until we are totally done syncing the records.
$this->attachNew($records, $current, false); $changes = array_merge(
$changes, $this->attachNew($records, $current, false)
);
$this->touchIfTouching(); if (count($changes['attached']) || count($changes['updated']))
{
$this->touchIfTouching();
}
return $changes;
} }
/** /**
* Attach all of the IDs that aren't in the current array. * Update an existing pivot record on the table.
* *
* @param array $records * @param mixed $id
* @param array $current * @param array $attributes
* @param bool $touch * @param bool $touch
* @return void * @return void
*/ */
protected function attachNew(array $records, array $current, $touch = true) public function updateExistingPivot($id, array $attributes, $touch = true)
{ {
foreach ($records as $id => $attributes) // Do nothing, we have no pivot table.
{
// If the ID is not in the list of existing pivot IDs, we will insert a new pivot
// record, otherwise, we will just update this existing record on this joining
// table, so that the developers will easily update these records pain free.
if ( ! in_array($id, $current))
{
$this->attach($id, $attributes, $touch);
}
}
} }
/** /**
...@@ -108,25 +154,37 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -108,25 +154,37 @@ class BelongsToMany extends EloquentBelongsToMany {
*/ */
public function attach($id, array $attributes = array(), $touch = true) public function attach($id, array $attributes = array(), $touch = true)
{ {
if ($id instanceof Model) $id = $id->getKey(); if ($id instanceof Model)
{
$model = $id; $id = $model->getKey();
}
$records = $this->createAttachRecords((array) $id, $attributes); $records = $this->createAttachRecords((array) $id, $attributes);
// Get the ID's to attach to the two documents // Get the ids to attach to the parent and related model.
$otherIds = array_pluck($records, $this->otherKey); $otherIds = array_pluck($records, $this->otherKey);
$foreignIds = array_pluck($records, $this->foreignKey); $foreignIds = array_pluck($records, $this->foreignKey);
// Attach to the parent model // Attach the new ids to the parent model.
$this->parent->push($this->otherKey, $otherIds[0]); $this->parent->push($this->otherKey, $otherIds, true);
// Generate a new related query instance // If we have a model instance, we can psuh the ids to that model,
$query = $this->getNewRelatedQuery(); // so that the internal attributes are updated as well. Otherwise,
// we will just perform a regular database query.
if (isset($model))
{
// Attach the new ids to the related model.
$model->push($this->foreignKey, $foreignIds, true);
}
else
{
$query = $this->newRelatedQuery();
// Set contraints on the related query $query->where($this->related->getKeyName(), $id);
$query->where($this->related->getKeyName(), $id);
// Attach to the related model // Attach the new ids to the related model.
$query->push($this->foreignKey, $foreignIds[0]); $query->push($this->foreignKey, $foreignIds, true);
}
if ($touch) $this->touchIfTouching(); if ($touch) $this->touchIfTouching();
} }
...@@ -142,18 +200,15 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -142,18 +200,15 @@ class BelongsToMany extends EloquentBelongsToMany {
{ {
if ($ids instanceof Model) $ids = (array) $ids->getKey(); if ($ids instanceof Model) $ids = (array) $ids->getKey();
$query = $this->getNewRelatedQuery(); $query = $this->newRelatedQuery();
// 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;
// Pull each id from the parent. // Detach all ids from the parent model.
foreach ($ids as $id) $this->parent->pull($this->otherKey, $ids);
{
$this->parent->pull($this->otherKey, $id);
}
// Prepare the query to select all related objects. // Prepare the query to select all related objects.
if (count($ids) > 0) if (count($ids) > 0)
...@@ -196,11 +251,21 @@ class BelongsToMany extends EloquentBelongsToMany { ...@@ -196,11 +251,21 @@ class BelongsToMany extends EloquentBelongsToMany {
} }
/** /**
* Get a new related query. * Create a new query builder for the related model.
*
* @return \Illuminate\Database\Query\Builder
*/
protected function newPivotQuery()
{
return $this->newRelatedQuery();
}
/**
* Create a new query builder for the related model.
* *
* @return \Illuminate\Database\Query\Builder * @return \Illuminate\Database\Query\Builder
*/ */
public function getNewRelatedQuery() public function newRelatedQuery()
{ {
return $this->related->newQuery(); return $this->related->newQuery();
} }
......
<?php namespace Jenssegers\Mongodb\Relations; <?php namespace Jenssegers\Mongodb\Relations;
use MongoId;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Collection;
use MongoId;
class EmbedsMany extends EmbedsOneOrMany { class EmbedsMany extends EmbedsOneOrMany {
/** /**
* Get the results of the relationship. * Get the results of the relationship.
* *
* @return Illuminate\Database\Eloquent\Collection * @return Jenssegers\Mongodb\Eloquent\Collection
*/ */
public function getResults() public function getResults()
{ {
...@@ -19,54 +18,109 @@ class EmbedsMany extends EmbedsOneOrMany { ...@@ -19,54 +18,109 @@ class EmbedsMany extends EmbedsOneOrMany {
} }
/** /**
* Simulate order by method. * Save a new model and attach it to the parent model.
* *
* @param string $column * @param \Illuminate\Database\Eloquent\Model $model
* @param string $direction * @return \Illuminate\Database\Eloquent\Model
* @return Illuminate\Database\Eloquent\Collection
*/ */
public function orderBy($column, $direction = 'asc') public function performInsert(Model $model, array $values)
{ {
$descending = strtolower($direction) == 'desc'; // Generate a new key if needed.
if ($model->getKeyName() == '_id' and ! $model->getKey())
{
$model->setAttribute('_id', new MongoId);
}
// For deeply nested documents, let the parent handle the changes.
if ($this->isNested())
{
$this->associate($model);
return $this->getResults()->sortBy($column, SORT_REGULAR, $descending); return $this->parent->save();
}
// Push the new model to the database.
$result = $this->getBaseQuery()->push($this->localKey, $model->getAttributes(), true);
// Attach the model to its parent.
if ($result) $this->associate($model);
return $result ? $model : false;
} }
/** /**
* Associate the model instance to the given parent, without saving it to the database. * Save an existing model and attach it to the parent model.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model * @return Model|bool
*/ */
public function associate(Model $model) public function performUpdate(Model $model, array $values)
{ {
if ( ! $this->contains($model)) // For deeply nested documents, let the parent handle the changes.
{ if ($this->isNested())
return $this->associateNew($model);
}
else
{ {
return $this->associateExisting($model); $this->associate($model);
return $this->parent->save();
} }
// Get the correct foreign key value.
$foreignKey = $this->getForeignKeyValue($model);
// Use array dot notation for better update behavior.
$values = array_dot($model->getDirty(), $this->localKey . '.$.');
// Update document in database.
$result = $this->getBaseQuery()->where($this->localKey . '.' . $model->getKeyName(), $foreignKey)
->update($values);
// Attach the model to its parent.
if ($result) $this->associate($model);
return $result ? $model : false;
} }
/** /**
* Destroy the embedded models for the given IDs. * Delete an existing model and detach it from the parent model.
* *
* @param mixed $ids * @param Model $model
* @return int * @return int
*/ */
public function destroy($ids = array()) public function performDelete(Model $model)
{ {
$ids = $this->getIdsArrayFrom($ids); // For deeply nested documents, let the parent handle the changes.
if ($this->isNested())
{
$this->dissociate($model);
// Get all models matching the given ids. return $this->parent->save();
$models = $this->get()->only($ids); }
// Pull the documents from the database. // Get the correct foreign key value.
foreach ($models as $model) $foreignKey = $this->getForeignKeyValue($model);
$result = $this->getBaseQuery()->pull($this->localKey, array($model->getKeyName() => $foreignKey));
if ($result) $this->dissociate($model);
return $result;
}
/**
* Associate the model instance to the given parent, without saving it to the database.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
public function associate(Model $model)
{
if ( ! $this->contains($model))
{
return $this->associateNew($model);
}
else
{ {
$this->performDelete($model); return $this->associateExisting($model);
} }
} }
...@@ -81,6 +135,7 @@ class EmbedsMany extends EmbedsOneOrMany { ...@@ -81,6 +135,7 @@ class EmbedsMany extends EmbedsOneOrMany {
$ids = $this->getIdsArrayFrom($ids); $ids = $this->getIdsArrayFrom($ids);
$records = $this->getEmbedded(); $records = $this->getEmbedded();
$primaryKey = $this->related->getKeyName(); $primaryKey = $this->related->getKeyName();
// Remove the document from the parent model. // Remove the document from the parent model.
...@@ -100,6 +155,30 @@ class EmbedsMany extends EmbedsOneOrMany { ...@@ -100,6 +155,30 @@ class EmbedsMany extends EmbedsOneOrMany {
return count($ids); return count($ids);
} }
/**
* Destroy the embedded models for the given IDs.
*
* @param mixed $ids
* @return int
*/
public function destroy($ids = array())
{
$count = 0;
$ids = $this->getIdsArrayFrom($ids);
// Get all models matching the given ids.
$models = $this->getResults()->only($ids);
// Pull the documents from the database.
foreach ($models as $model)
{
if ($model->delete()) $count++;
}
return $count;
}
/** /**
* Delete all embedded models. * Delete all embedded models.
* *
...@@ -110,17 +189,7 @@ class EmbedsMany extends EmbedsOneOrMany { ...@@ -110,17 +189,7 @@ class EmbedsMany extends EmbedsOneOrMany {
// Overwrite the local key with an empty array. // Overwrite the local key with an empty array.
$result = $this->query->update(array($this->localKey => array())); $result = $this->query->update(array($this->localKey => array()));
// If the update query was successful, we will remove the embedded records if ($result) $this->setEmbedded(array());
// of the parent instance.
if ($result)
{
$count = $this->count();
$this->setEmbedded(array());
// Return the number of deleted embedded records.
return $count;
}
return $result; return $result;
} }
...@@ -147,78 +216,6 @@ class EmbedsMany extends EmbedsOneOrMany { ...@@ -147,78 +216,6 @@ class EmbedsMany extends EmbedsOneOrMany {
return $this->save($model); return $this->save($model);
} }
/**
* Save a new model and attach it to the parent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model
*/
protected function performInsert(Model $model)
{
// Create a new key if needed.
if ( ! $model->getAttribute('_id'))
{
$model->setAttribute('_id', new MongoId);
}
// Push the new model to the database.
$result = $this->query->push($this->localKey, $model->getAttributes(), true);
// Associate the new model to the parent.
if ($result) $this->associateNew($model);
return $result ? $model : false;
}
/**
* Save an existing model and attach it to the parent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return Model|bool
*/
protected function performUpdate(Model $model)
{
// Get the correct foreign key value.
$id = $this->getForeignKeyValue($model);
// Update document in database.
$result = $this->query->where($this->localKey . '.' . $model->getKeyName(), $id)
->update(array($this->localKey . '.$' => $model->getAttributes()));
// Update the related model in the parent instance
if ($result) $this->associateExisting($model);
return $result ? $model : false;
}
/**
* Remove an existing model and detach it from the parent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return bool
*/
protected function performDelete(Model $model)
{
if ($this->fireModelEvent($model, 'deleting') === false) return false;
// Get the correct foreign key value.
$id = $this->getForeignKeyValue($model);
$result = $this->query->pull($this->localKey, array($model->getKeyName() => $id));
if ($result)
{
$this->fireModelEvent($model, 'deleted', false);
// Update the related model in the parent instance
$this->dissociate($model);
return true;
}
return false;
}
/** /**
* Associate a new model instance to the given parent, without saving it to the database. * Associate a new model instance to the given parent, without saving it to the database.
* *
...@@ -235,12 +232,10 @@ class EmbedsMany extends EmbedsOneOrMany { ...@@ -235,12 +232,10 @@ class EmbedsMany extends EmbedsOneOrMany {
$records = $this->getEmbedded(); $records = $this->getEmbedded();
// Add the document to the parent model. // Add the new model to the embedded documents.
$records[] = $model->getAttributes(); $records[] = $model->getAttributes();
$this->setEmbedded($records); return $this->setEmbedded($records);
return $model;
} }
/** /**
...@@ -255,6 +250,7 @@ class EmbedsMany extends EmbedsOneOrMany { ...@@ -255,6 +250,7 @@ class EmbedsMany extends EmbedsOneOrMany {
$records = $this->getEmbedded(); $records = $this->getEmbedded();
$primaryKey = $this->related->getKeyName(); $primaryKey = $this->related->getKeyName();
$key = $model->getKey(); $key = $model->getKey();
// Replace the document in the parent model. // Replace the document in the parent model.
...@@ -267,9 +263,29 @@ class EmbedsMany extends EmbedsOneOrMany { ...@@ -267,9 +263,29 @@ class EmbedsMany extends EmbedsOneOrMany {
} }
} }
$this->setEmbedded($records); return $this->setEmbedded($records);
}
/**
* Get a paginator for the "select" statement.
*
* @param int $perPage
* @param array $columns
* @return \Illuminate\Pagination\Paginator
*/
public function paginate($perPage = null, $columns = array('*'))
{
$perPage = $perPage ?: $this->related->getPerPage();
$paginator = $this->related->getConnection()->getPaginator();
$results = $this->getEmbedded();
$start = ($paginator->getCurrentPage() - 1) * $perPage;
$sliced = array_slice($results, $start, $perPage);
return $model; return $paginator->make($sliced, count($results), $perPage);
} }
/** /**
...@@ -290,7 +306,7 @@ class EmbedsMany extends EmbedsOneOrMany { ...@@ -290,7 +306,7 @@ class EmbedsMany extends EmbedsOneOrMany {
*/ */
protected function setEmbedded($models) protected function setEmbedded($models)
{ {
if (! is_array($models)) $models = array($models); if ( ! is_array($models)) $models = array($models);
return parent::setEmbedded(array_values($models)); return parent::setEmbedded(array_values($models));
} }
...@@ -305,7 +321,7 @@ class EmbedsMany extends EmbedsOneOrMany { ...@@ -305,7 +321,7 @@ class EmbedsMany extends EmbedsOneOrMany {
public function __call($method, $parameters) public function __call($method, $parameters)
{ {
// Collection methods // Collection methods
if (method_exists('Illuminate\Database\Eloquent\Collection', $method)) if (method_exists('Jenssegers\Mongodb\Eloquent\Collection', $method))
{ {
return call_user_func_array(array($this->getResults(), $method), $parameters); return call_user_func_array(array($this->getResults(), $method), $parameters);
} }
......
<?php namespace Jenssegers\Mongodb\Relations; <?php namespace Jenssegers\Mongodb\Relations;
use MongoId;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Collection;
use MongoId;
class EmbedsOne extends EmbedsOneOrMany { class EmbedsOne extends EmbedsOneOrMany {
/** /**
* Get the results of the relationship. * Get the results of the relationship.
* *
* @return Illuminate\Database\Eloquent\Collection * @return \Illuminate\Database\Eloquent\Model
*/ */
public function getResults() public function getResults()
{ {
...@@ -19,75 +18,105 @@ class EmbedsOne extends EmbedsOneOrMany { ...@@ -19,75 +18,105 @@ class EmbedsOne extends EmbedsOneOrMany {
} }
/** /**
* Check if a model is already embedded. * Save a new model and attach it to the parent model.
* *
* @param mixed $key * @param \Illuminate\Database\Eloquent\Model $model
* @return bool * @return \Illuminate\Database\Eloquent\Model
*/ */
public function contains($key) public function performInsert(Model $model, array $values)
{ {
if ($key instanceof Model) $key = $key->getKey(); // Generate a new key if needed.
if ($model->getKeyName() == '_id' and ! $model->getKey())
{
$model->setAttribute('_id', new MongoId);
}
$embedded = $this->getEmbedded(); // For deeply nested documents, let the parent handle the changes.
if ($this->isNested())
{
$this->associate($model);
$primaryKey = $this->related->getKeyName(); return $this->parent->save();
}
$result = $this->getBaseQuery()->update(array($this->localKey => $model->getAttributes()));
// Attach the model to its parent.
if ($result) $this->associate($model);
return ($embedded and $embedded[$primaryKey] == $key); return $result ? $model : false;
} }
/** /**
* Associate the model instance to the given parent, without saving it to the database. * Save an existing model and attach it to the parent model.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model * @return Model|bool
*/ */
public function associate(Model $model) public function performUpdate(Model $model, array $values)
{ {
// Create a new key if needed. if ($this->isNested())
if ( ! $model->getAttribute('_id'))
{ {
$model->setAttribute('_id', new MongoId); $this->associate($model);
return $this->parent->save();
} }
$this->setEmbedded($model->getAttributes()); // Use array dot notation for better update behavior.
$values = array_dot($model->getDirty(), $this->localKey . '.');
return $model; $result = $this->getBaseQuery()->update($values);
// Attach the model to its parent.
if ($result) $this->associate($model);
return $result ? $model : false;
} }
/** /**
* Save a new model and attach it to the parent model. * Delete an existing model and detach it from the parent model.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param Model $model
* @return \Illuminate\Database\Eloquent\Model * @return int
*/ */
protected function performInsert(Model $model) public function performDelete(Model $model)
{ {
// Create a new key if needed. // For deeply nested documents, let the parent handle the changes.
if ( ! $model->getAttribute('_id')) if ($this->isNested())
{ {
$model->setAttribute('_id', new MongoId); $this->dissociate($model);
return $this->parent->save();
} }
$result = $this->query->update(array($this->localKey => $model->getAttributes())); // Overwrite the local key with an empty array.
$result = $this->getBaseQuery()->update(array($this->localKey => null));
if ($result) $this->associate($model); // Detach the model from its parent.
if ($result) $this->dissociate();
return $result ? $model : false; return $result;
} }
/** /**
* Save an existing model and attach it to the parent model. * Attach the model to its parent.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param \Illuminate\Database\Eloquent\Model $model
* @return Model|bool * @return \Illuminate\Database\Eloquent\Model
*/ */
protected function performUpdate(Model $model) public function associate(Model $model)
{ {
$result = $this->query->update(array($this->localKey => $model->getAttributes())); return $this->setEmbedded($model->getAttributes());
}
if ($result) $this->associate($model);
return $result ? $model : false; /**
* Detach the model from its parent.
*
* @return \Illuminate\Database\Eloquent\Model
*/
public function dissociate()
{
return $this->setEmbedded(null);
} }
/** /**
...@@ -97,22 +126,9 @@ class EmbedsOne extends EmbedsOneOrMany { ...@@ -97,22 +126,9 @@ class EmbedsOne extends EmbedsOneOrMany {
*/ */
public function delete() public function delete()
{ {
// Overwrite the local key with an empty array. $model = $this->getResults();
$result = $this->query->update(array($this->localKey => null));
// If the update query was successful, we will remove the embedded records return $this->performDelete($model);
// of the parent instance.
if ($result)
{
$count = $this->count();
$this->setEmbedded(null);
// Return the number of deleted embedded records.
return $count;
}
return $result;
} }
} }
<?php namespace Jenssegers\Mongodb\Relations; <?php namespace Jenssegers\Mongodb\Relations;
use MongoId;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection as BaseCollection;
use MongoId; use Jenssegers\Mongodb\Eloquent\Collection;
abstract class EmbedsOneOrMany extends Relation { abstract class EmbedsOneOrMany extends Relation {
...@@ -48,6 +49,12 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -48,6 +49,12 @@ abstract class EmbedsOneOrMany extends Relation {
$this->foreignKey = $foreignKey; $this->foreignKey = $foreignKey;
$this->relation = $relation; $this->relation = $relation;
// If this is a nested relation, we need to get the parent query instead.
if ($parentRelation = $this->getParentRelation())
{
$this->query = $parentRelation->getQuery();
}
$this->addConstraints(); $this->addConstraints();
} }
...@@ -60,7 +67,7 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -60,7 +67,7 @@ abstract class EmbedsOneOrMany extends Relation {
{ {
if (static::$constraints) if (static::$constraints)
{ {
$this->query->where($this->parent->getKeyName(), '=', $this->parent->getKey()); $this->query->where($this->getQualifiedParentKeyName(), '=', $this->getParentKey());
} }
} }
...@@ -86,6 +93,8 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -86,6 +93,8 @@ abstract class EmbedsOneOrMany extends Relation {
{ {
foreach ($models as $model) foreach ($models as $model)
{ {
$model->setParentRelation($this);
$model->setRelation($relation, $this->related->newCollection()); $model->setRelation($relation, $this->related->newCollection());
} }
...@@ -100,12 +109,14 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -100,12 +109,14 @@ abstract class EmbedsOneOrMany extends Relation {
* @param string $relation * @param string $relation
* @return array * @return array
*/ */
public function match(array $models, Collection $results, $relation) public function match(array $models, BaseCollection $results, $relation)
{ {
foreach ($models as $model) foreach ($models as $model)
{ {
$results = $model->$relation()->getResults(); $results = $model->$relation()->getResults();
$model->setParentRelation($this);
$model->setRelation($relation, $results); $model->setRelation($relation, $results);
} }
...@@ -115,7 +126,7 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -115,7 +126,7 @@ abstract class EmbedsOneOrMany extends Relation {
/** /**
* Shorthand to get the results of the relationship. * Shorthand to get the results of the relationship.
* *
* @return Illuminate\Database\Eloquent\Collection * @return Jenssegers\Mongodb\Eloquent\Collection
*/ */
public function get() public function get()
{ {
...@@ -140,44 +151,22 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -140,44 +151,22 @@ abstract class EmbedsOneOrMany extends Relation {
*/ */
public function save(Model $model) public function save(Model $model)
{ {
if ($this->fireModelEvent($model, 'saving') === false) return false; $model->setParentRelation($this);
$this->updateTimestamps($model);
// Attach a new model.
if ( ! $this->contains($model))
{
if ($this->fireModelEvent($model, 'creating') === false) return false;
$result = $this->performInsert($model);
if ($result)
{
$this->fireModelEvent($model, 'created', false);
// Mark model as existing
$model->exists = true;
}
}
// Update an existing model. return $model->save() ? $model : false;
else }
{
if ($this->fireModelEvent($model, 'updating') === false) return false;
$result = $this->performUpdate($model);
if ($result) $this->fireModelEvent($model, 'updated', false);
}
if ($result)
{
$this->fireModelEvent($result, 'saved', false);
return $result; /**
} * Attach an array of models to the parent instance.
*
* @param array $models
* @return array
*/
public function saveMany(array $models)
{
array_walk($models, array($this, 'save'));
return false; return $models;
} }
/** /**
...@@ -191,24 +180,13 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -191,24 +180,13 @@ abstract class EmbedsOneOrMany extends Relation {
// Here we will set the raw attributes to avoid hitting the "fill" method so // Here we will set the raw attributes to avoid hitting the "fill" method so
// that we do not have to worry about a mass accessor rules blocking sets // that we do not have to worry about a mass accessor rules blocking sets
// on the models. Otherwise, some of these attributes will not get set. // on the models. Otherwise, some of these attributes will not get set.
$instance = $this->related->newInstance(); $instance = $this->related->newInstance($attributes);
$instance->setRawAttributes($attributes);
return $this->save($instance); $instance->setParentRelation($this);
}
/** $instance->save();
* Attach an array of models to the parent instance.
*
* @param array $models
* @return array
*/
public function saveMany(array $models)
{
array_walk($models, array($this, 'save'));
return $models; return $instance;
} }
/** /**
...@@ -219,7 +197,12 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -219,7 +197,12 @@ abstract class EmbedsOneOrMany extends Relation {
*/ */
public function createMany(array $records) public function createMany(array $records)
{ {
$instances = array_map(array($this, 'create'), $records); $instances = array();
foreach ($records as $record)
{
$instances[] = $this->create($record);
}
return $instances; return $instances;
} }
...@@ -232,7 +215,7 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -232,7 +215,7 @@ abstract class EmbedsOneOrMany extends Relation {
*/ */
protected function getIdsArrayFrom($ids) protected function getIdsArrayFrom($ids)
{ {
if (! is_array($ids)) $ids = array($ids); if ( ! is_array($ids)) $ids = array($ids);
foreach ($ids as &$id) foreach ($ids as &$id)
{ {
...@@ -242,26 +225,6 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -242,26 +225,6 @@ abstract class EmbedsOneOrMany extends Relation {
return $ids; return $ids;
} }
/**
* Create a related model instanced.
*
* @param array $attributes [description]
* @return [type] [description]
*/
protected function toModel($attributes = array())
{
if (is_null($attributes)) return null;
$model = $this->related->newFromBuilder((array) $attributes);
// Attatch the parent relation to the embedded model.
$model->setRelation($this->foreignKey, $this->parent);
$model->setHidden(array_merge($model->getHidden(), array($this->foreignKey)));
return $model;
}
/** /**
* Get the embedded records array. * Get the embedded records array.
* *
...@@ -278,43 +241,20 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -278,43 +241,20 @@ abstract class EmbedsOneOrMany extends Relation {
/** /**
* Set the embedded records array. * Set the embedded records array.
* *
* @param array $models * @param array $records
* @return void * @return \Illuminate\Database\Eloquent\Model
*/ */
protected function setEmbedded($data) protected function setEmbedded($records)
{ {
$attributes = $this->parent->getAttributes(); $attributes = $this->parent->getAttributes();
$attributes[$this->localKey] = $data; $attributes[$this->localKey] = $records;
// Set raw attributes to skip mutators. // Set raw attributes to skip mutators.
$this->parent->setRawAttributes($attributes); $this->parent->setRawAttributes($attributes);
// Set the relation on the parent. // Set the relation on the parent.
$this->parent->setRelation($this->relation, $this->getResults()); return $this->parent->setRelation($this->relation, $this->getResults());
}
/**
* Update the creation and update timestamps.
*
* @return void
*/
protected function updateTimestamps(Model $model)
{
// Check if this model uses timestamps first.
if ( ! $model->timestamps) return;
$time = $model->freshTimestamp();
if ( ! $model->isDirty(Model::UPDATED_AT))
{
$model->setUpdatedAt($time);
}
if ( ! $model->exists && ! $model->isDirty(Model::CREATED_AT))
{
$model->setCreatedAt($time);
}
} }
/** /**
...@@ -338,13 +278,12 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -338,13 +278,12 @@ abstract class EmbedsOneOrMany extends Relation {
* Convert an array of records to a Collection. * Convert an array of records to a Collection.
* *
* @param array $records * @param array $records
* @return Illuminate\Database\Eloquent\Collection * @return Jenssegers\Mongodb\Eloquent\Collection
*/ */
protected function toCollection(array $records = array()) protected function toCollection(array $records = array())
{ {
$models = array(); $models = array();
// Wrap records in model objects.
foreach ($records as $attributes) foreach ($records as $attributes)
{ {
$models[] = $this->toModel($attributes); $models[] = $this->toModel($attributes);
...@@ -355,30 +294,113 @@ abstract class EmbedsOneOrMany extends Relation { ...@@ -355,30 +294,113 @@ abstract class EmbedsOneOrMany extends Relation {
$models = $this->eagerLoadRelations($models); $models = $this->eagerLoadRelations($models);
} }
return $this->related->newCollection($models); return new Collection($models);
} }
/** /**
* Fire the given event for the given model. * Create a related model instanced.
* *
* @param string $event * @param array $attributes
* @param bool $halt * @return \Illuminate\Database\Eloquent\Model
* @return mixed
*/ */
protected function fireModelEvent(Model $model, $event, $halt = true) protected function toModel($attributes = array())
{ {
$dispatcher = $model->getEventDispatcher(); if (is_null($attributes)) return null;
if ( is_null($dispatcher)) return true; $model = $this->related->newFromBuilder((array) $attributes);
// We will append the names of the class to the event to distinguish it from $model->setParentRelation($this);
// other model events that are fired, allowing us to listen on each model
// event set individually instead of catching event for all the models.
$event = "eloquent.{$event}: ".get_class($model);
$method = $halt ? 'until' : 'fire'; $model->setRelation($this->foreignKey, $this->parent);
// If you remove this, you will get segmentation faults!
$model->setHidden(array_merge($model->getHidden(), array($this->foreignKey)));
return $model;
}
/**
* Get the relation instance of the parent.
*
* @return Illuminate\Database\Eloquent\Relations\Relation
*/
protected function getParentRelation()
{
return $this->parent->getParentRelation();
}
/**
* Get the underlying query for the relation.
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function getQuery()
{
// Because we are sharing this relation instance to models, we need
// to make sure we use separate query instances.
return clone $this->query;
}
return $dispatcher->$method($event, $model); /**
* Get the base query builder driving the Eloquent builder.
*
* @return \Illuminate\Database\Query\Builder
*/
public function getBaseQuery()
{
// Because we are sharing this relation instance to models, we need
// to make sure we use separate query instances.
return clone $this->query->getQuery();
}
/**
* Check if this relation is nested in another relation.
*
* @return boolean
*/
protected function isNested()
{
return $this->getParentRelation() != null;
}
/**
* Get the fully qualified local key name.
*
* @return string
*/
protected function getPathHierarchy($glue = '.')
{
if ($parentRelation = $this->getParentRelation())
{
return $parentRelation->getPathHierarchy($glue) . $glue . $this->localKey;
}
return $this->localKey;
}
/**
* Get the parent's fully qualified key name.
*
* @return string
*/
protected function getQualifiedParentKeyName()
{
if ($parentRelation = $this->getParentRelation())
{
return $parentRelation->getPathHierarchy() . '.' . $this->parent->getKeyName();
}
return $this->parent->getKeyName();
}
/**
* Get the primary key value of the parent.
*
* @return string
*/
protected function getParentKey()
{
return $this->parent->getKey();
} }
} }
...@@ -20,4 +20,14 @@ class HasMany extends EloquentHasMany { ...@@ -20,4 +20,14 @@ class HasMany extends EloquentHasMany {
return $query->select($this->getHasCompareKey())->where($this->getHasCompareKey(), 'exists', true); return $query->select($this->getHasCompareKey())->where($this->getHasCompareKey(), 'exists', true);
} }
/**
* Get the plain foreign key.
*
* @return string
*/
public function getPlainForeignKey()
{
return $this->getForeignKey();
}
} }
...@@ -20,4 +20,14 @@ class HasOne extends EloquentHasOne { ...@@ -20,4 +20,14 @@ class HasOne extends EloquentHasOne {
return $query->select($this->getHasCompareKey())->where($this->getHasCompareKey(), 'exists', true); return $query->select($this->getHasCompareKey())->where($this->getHasCompareKey(), 'exists', true);
} }
/**
* Get the plain foreign key.
*
* @return string
*/
public function getPlainForeignKey()
{
return $this->getForeignKey();
}
} }
...@@ -36,6 +36,7 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint { ...@@ -36,6 +36,7 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint {
public function __construct(Connection $connection, $collection) public function __construct(Connection $connection, $collection)
{ {
$this->connection = $connection; $this->connection = $connection;
$this->collection = $connection->getCollection($collection); $this->collection = $connection->getCollection($collection);
} }
...@@ -50,11 +51,12 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint { ...@@ -50,11 +51,12 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint {
{ {
$columns = $this->fluent($columns); $columns = $this->fluent($columns);
// Columns are passed as a default array // Columns are passed as a default array.
if (is_array($columns) && is_int(key($columns))) if (is_array($columns) && is_int(key($columns)))
{ {
// Transform the columns to the required array format // Transform the columns to the required array format.
$transform = array(); $transform = array();
foreach ($columns as $column) foreach ($columns as $column)
{ {
$transform[$column] = 1; $transform[$column] = 1;
...@@ -78,11 +80,12 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint { ...@@ -78,11 +80,12 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint {
{ {
$columns = $this->fluent($columns); $columns = $this->fluent($columns);
// Columns are passed as a default array // Columns are passed as a default array.
if (is_array($columns) && is_int(key($columns))) if (is_array($columns) && is_int(key($columns)))
{ {
// Transform the columns to the required array format // Transform the columns to the required array format.
$transform = array(); $transform = array();
foreach ($columns as $column) foreach ($columns as $column)
{ {
$transform[$column] = 1; $transform[$column] = 1;
...@@ -105,6 +108,7 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint { ...@@ -105,6 +108,7 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint {
public function unique($columns = null, $name = null) public function unique($columns = null, $name = null)
{ {
$columns = $this->fluent($columns); $columns = $this->fluent($columns);
$this->index($columns, array('unique' => true)); $this->index($columns, array('unique' => true));
return $this; return $this;
...@@ -119,6 +123,7 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint { ...@@ -119,6 +123,7 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint {
public function background($columns = null) public function background($columns = null)
{ {
$columns = $this->fluent($columns); $columns = $this->fluent($columns);
$this->index($columns, array('background' => true)); $this->index($columns, array('background' => true));
return $this; return $this;
...@@ -149,6 +154,7 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint { ...@@ -149,6 +154,7 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint {
public function expire($columns, $seconds) public function expire($columns, $seconds)
{ {
$columns = $this->fluent($columns); $columns = $this->fluent($columns);
$this->index($columns, array('expireAfterSeconds' => $seconds)); $this->index($columns, array('expireAfterSeconds' => $seconds));
return $this; return $this;
...@@ -163,8 +169,9 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint { ...@@ -163,8 +169,9 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint {
{ {
$collection = $this->collection->getName(); $collection = $this->collection->getName();
// Ensure the collection is created
$db = $this->connection->getMongoDB(); $db = $this->connection->getMongoDB();
// Ensure the collection is created.
$db->createCollection($collection); $db->createCollection($collection);
} }
...@@ -189,6 +196,7 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint { ...@@ -189,6 +196,7 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint {
protected function addColumn($type, $name, array $parameters = array()) protected function addColumn($type, $name, array $parameters = array())
{ {
$this->fluent($name); $this->fluent($name);
return $this; return $this;
} }
...@@ -221,6 +229,7 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint { ...@@ -221,6 +229,7 @@ class Blueprint extends \Illuminate\Database\Schema\Blueprint {
*/ */
public function __call($method, $args) public function __call($method, $args)
{ {
// Dummy.
return $this; return $this;
} }
......
<?php
class AuthTest extends TestCase {
public function tearDown()
{
User::truncate();
DB::collection('password_reminders')->truncate();
}
public function testAuthAttempt()
{
$user = User::create(array(
'name' => 'John Doe',
'email' => 'john@doe.com',
'password' => Hash::make('foobar')
));
$this->assertTrue(Auth::attempt(array('email' => 'john@doe.com', 'password' => 'foobar'), true));
$this->assertTrue(Auth::check());
}
public function testRemind()
{
$mailer = Mockery::mock('Illuminate\Mail\Mailer');
$this->app->instance('mailer', $mailer);
$user = User::create(array(
'name' => 'John Doe',
'email' => 'john@doe.com',
'password' => Hash::make('foobar')
));
$mailer->shouldReceive('send')->once();
Password::remind(array('email' => 'john@doe.com'));
$this->assertEquals(1, DB::collection('password_reminders')->count());
$reminder = DB::collection('password_reminders')->first();
$this->assertEquals('john@doe.com', $reminder['email']);
$this->assertNotNull($reminder['token']);
$this->assertInstanceOf('MongoDate', $reminder['created_at']);
$credentials = array(
'email' => 'john@doe.com',
'password' => 'foobar',
'password_confirmation' => 'foobar',
'token' => $reminder['token']
);
$response = Password::reset($credentials, function($user, $password)
{
$user->password = Hash::make($password);
$user->save();
});
$this->assertEquals('reminders.reset', $response);
$this->assertEquals(0, DB::collection('password_reminders')->count());
}
public function testDeprecatedRemind()
{
$mailer = Mockery::mock('Illuminate\Mail\Mailer');
$this->app->instance('mailer', $mailer);
$user = User::create(array(
'name' => 'John Doe',
'email' => 'john@doe.com',
'password' => Hash::make('foobar')
));
$mailer->shouldReceive('send')->once();
Password::remind(array('email' => 'john@doe.com'));
DB::collection('password_reminders')->update(array('created_at' => new DateTime));
$reminder = DB::collection('password_reminders')->first();
$this->assertTrue(is_array($reminder['created_at']));
$credentials = array(
'email' => 'john@doe.com',
'password' => 'foobar',
'password_confirmation' => 'foobar',
'token' => $reminder['token']
);
$response = Password::reset($credentials, function($user, $password)
{
$user->password = Hash::make($password);
$user->save();
});
$this->assertEquals('reminders.reset', $response);
$this->assertEquals(0, DB::collection('password_reminders')->count());
}
}
...@@ -29,7 +29,8 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -29,7 +29,8 @@ class EmbeddedRelationsTest extends TestCase {
$address = $user->addresses()->save($address); $address = $user->addresses()->save($address);
$address->unsetEventDispatcher(); $address->unsetEventDispatcher();
$this->assertNotNull($user->_addresses); $this->assertNotNull($user->addresses);
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $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);
$this->assertInstanceOf('DateTime', $address->updated_at); $this->assertInstanceOf('DateTime', $address->updated_at);
...@@ -73,6 +74,7 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -73,6 +74,7 @@ class EmbeddedRelationsTest extends TestCase {
$user = User::find($user->_id); $user = User::find($user->_id);
$user->addresses()->save(new Address(array('city' => 'Bruxelles'))); $user->addresses()->save(new Address(array('city' => 'Bruxelles')));
$this->assertEquals(array('London', 'New York', 'Bruxelles'), $user->addresses->lists('city')); $this->assertEquals(array('London', 'New York', 'Bruxelles'), $user->addresses->lists('city'));
$address = $user->addresses[1]; $address = $user->addresses[1];
$address->city = "Manhattan"; $address->city = "Manhattan";
$user->addresses()->save($address); $user->addresses()->save($address);
...@@ -82,6 +84,29 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -82,6 +84,29 @@ class EmbeddedRelationsTest extends TestCase {
$this->assertEquals(array('London', 'Manhattan', 'Bruxelles'), $freshUser->addresses->lists('city')); $this->assertEquals(array('London', 'Manhattan', 'Bruxelles'), $freshUser->addresses->lists('city'));
} }
public function testEmbedsManySaveModel()
{
$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->save();
$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 = 'Paris';
$address->save();
}
public function testEmbedsToArray() public function testEmbedsToArray()
{ {
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
...@@ -97,8 +122,7 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -97,8 +122,7 @@ class EmbeddedRelationsTest extends 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 = $user->addresses()->associate($address); $user->addresses()->associate($address);
$this->assertNotNull($user->_addresses);
$this->assertEquals(array('London'), $user->addresses->lists('city')); $this->assertEquals(array('London'), $user->addresses->lists('city'));
$this->assertNotNull($address->_id); $this->assertNotNull($address->_id);
...@@ -187,8 +211,8 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -187,8 +211,8 @@ class EmbeddedRelationsTest extends TestCase {
$address = $user->addresses->first(); $address = $user->addresses->first();
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher')); $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('until')->once()->with('eloquent.deleting: '.get_class($address), Mockery::type('Address'))->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.deleted: '.get_class($address), Mockery::mustBe($address)); $events->shouldReceive('fire')->once()->with('eloquent.deleted: '.get_class($address), Mockery::type('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'));
...@@ -217,6 +241,32 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -217,6 +241,32 @@ class EmbeddedRelationsTest extends TestCase {
$this->assertEquals(array('Bristol'), $user->addresses->lists('city')); $this->assertEquals(array('Bristol'), $user->addresses->lists('city'));
} }
public function testEmbedsManyDelete()
{
$user = User::create(array('name' => 'John Doe'));
$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::type('Address'))->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.deleted: '.get_class($address), Mockery::type('Address'));
$address->delete();
$this->assertEquals(2, $user->addresses()->count());
$this->assertEquals(2, $user->addresses->count());
$address->unsetEventDispatcher();
$address = $user->addresses->first();
$address->delete();
$user = User::where('name', 'John Doe')->first();
$this->assertEquals(1, $user->addresses()->count());
$this->assertEquals(1, $user->addresses->count());
}
public function testEmbedsManyDissociate() public function testEmbedsManyDissociate()
{ {
$user = User::create(array()); $user = User::create(array());
...@@ -328,16 +378,18 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -328,16 +378,18 @@ class EmbeddedRelationsTest extends TestCase {
$user = User::find($user1->id); $user = User::find($user1->id);
$relations = $user->getRelations(); $relations = $user->getRelations();
$this->assertFalse(array_key_exists('addresses', $relations)); $this->assertFalse(array_key_exists('addresses', $relations));
$this->assertArrayNotHasKey('addresses', $user->toArray()); $this->assertArrayHasKey('addresses', $user->toArray());
$this->assertTrue(is_array($user->toArray()['addresses']));
$user = User::with('addresses')->get()->first(); $user = User::with('addresses')->get()->first();
$relations = $user->getRelations(); $relations = $user->getRelations();
$this->assertTrue(array_key_exists('addresses', $relations)); $this->assertTrue(array_key_exists('addresses', $relations));
$this->assertEquals(2, $relations['addresses']->count()); $this->assertEquals(2, $relations['addresses']->count());
$this->assertArrayHasKey('addresses', $user->toArray()); $this->assertArrayHasKey('addresses', $user->toArray());
$this->assertTrue(is_array($user->toArray()['addresses']));
} }
public function testEmbedsManyDelete() public function testEmbedsManyDeleteAll()
{ {
$user1 = User::create(array('name' => 'John Doe')); $user1 = User::create(array('name' => 'John Doe'));
$user1->addresses()->save(new Address(array('city' => 'New York'))); $user1->addresses()->save(new Address(array('city' => 'New York')));
...@@ -364,14 +416,77 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -364,14 +416,77 @@ class EmbeddedRelationsTest extends TestCase {
public function testEmbedsManyCollectionMethods() public function testEmbedsManyCollectionMethods()
{ {
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
$user->addresses()->save(new Address(array('city' => 'New York'))); $user->addresses()->save(new Address(array('city' => 'Paris', 'country' => 'France', 'visited' => 4, 'created_at' => new DateTime('3 days ago'))));
$user->addresses()->save(new Address(array('city' => 'Paris'))); $user->addresses()->save(new Address(array('city' => 'Bruges', 'country' => 'Belgium', 'visited' => 7, 'created_at' => new DateTime('5 days ago'))));
$user->addresses()->save(new Address(array('city' => 'Brussels'))); $user->addresses()->save(new Address(array('city' => 'Brussels', 'country' => 'Belgium', 'visited' => 2, 'created_at' => new DateTime('4 days ago'))));
$user->addresses()->save(new Address(array('city' => 'Ghent', 'country' => 'Belgium', 'visited' => 13, 'created_at' => new DateTime('2 days ago'))));
$this->assertEquals(array('Paris', 'Bruges', 'Brussels', 'Ghent'), $user->addresses()->lists('city'));
$this->assertEquals(array('Bruges', 'Brussels', 'Ghent', 'Paris'), $user->addresses()->sortBy('city')->lists('city'));
$this->assertEquals(array('Bruges', 'Brussels', 'Ghent', 'Paris'), $user->addresses()->orderBy('city')->lists('city'));
$this->assertEquals(array('Paris', 'Ghent', 'Brussels', 'Bruges'), $user->addresses()->orderBy('city', 'desc')->lists('city'));
$this->assertEquals(array(), $user->addresses()->where('city', 'New York')->lists('city'));
$this->assertEquals(array('Bruges', 'Brussels', 'Ghent'), $user->addresses()->where('country', 'Belgium')->lists('city'));
$this->assertEquals(array('Ghent', 'Brussels', 'Bruges'), $user->addresses()->where('country', 'Belgium')->orderBy('city', 'desc')->lists('city'));
$results = $user->addresses->get(0);
$this->assertInstanceOf('Address', $results);
$results = $user->addresses()->where('country', 'Belgium')->get();
$this->assertInstanceOf('Jenssegers\Mongodb\Eloquent\Collection', $results);
$this->assertEquals(3, $results->count());
$results = $user->addresses()->where('country', '!=', 'Belgium')->get();
$this->assertEquals(1, $results->count());
$results = $user->addresses()->where('visited', '>', 4)->get();
$this->assertEquals(2, $results->count());
$results = $user->addresses()->where('visited', '<', 7)->get();
$this->assertEquals(2, $results->count());
$this->assertEquals(array('New York', 'Paris', 'Brussels'), $user->addresses()->lists('city')); $results = $user->addresses()->where('visited', '<=', 7)->get();
$this->assertEquals(array('Brussels', 'New York', 'Paris'), $user->addresses()->sortBy('city')->lists('city')); $this->assertEquals(3, $results->count());
$this->assertEquals(array('Brussels', 'New York', 'Paris'), $user->addresses()->orderBy('city')->lists('city'));
$this->assertEquals(array('Paris', 'New York', 'Brussels'), $user->addresses()->orderBy('city', 'desc')->lists('city')); $results = $user->addresses()->where('visited', '>=', 7)->get();
$this->assertEquals(2, $results->count());
$results = $user->addresses()->where('visited', 'between', array(4, 7))->get();
$this->assertEquals(2, $results->count());
$results = $user->addresses()->whereBetween('visited', array(4, 7))->get();
$this->assertEquals(2, $results->count());
$results = $user->addresses()->whereNotBetween('visited', array(4, 7))->get();
$this->assertEquals(2, $results->count());
$results = $user->addresses()->whereIn('visited', array(7, 13))->get();
$this->assertEquals(2, $results->count());
$results = $user->addresses()->whereNotIn('visited', array(7))->get();
$this->assertEquals(3, $results->count());
$results = $user->addresses()->whereNull('something')->get();
$this->assertEquals(4, $results->count());
$results = $user->addresses()->whereNotNull('visited')->get();
$this->assertEquals(4, $results->count());
$results = $user->addresses()->offset(1)->get();
$this->assertEquals(3, $results->count());
$results = $user->addresses()->skip(1)->get();
$this->assertEquals(3, $results->count());
$results = $user->addresses()->limit(2)->get();
$this->assertEquals(2, $results->count());
$result = $user->addresses()->latest()->first();
$this->assertEquals('Ghent', $result->city);
$result = $user->addresses()->oldest()->first();
$this->assertEquals('Bruges', $result->city);
} }
public function testEmbedsOne() public function testEmbedsOne()
...@@ -388,7 +503,7 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -388,7 +503,7 @@ class EmbeddedRelationsTest extends TestCase {
$father = $user->father()->save($father); $father = $user->father()->save($father);
$father->unsetEventDispatcher(); $father->unsetEventDispatcher();
$this->assertNotNull($user->_father); $this->assertNotNull($user->father);
$this->assertEquals('Mark Doe', $user->father->name); $this->assertEquals('Mark Doe', $user->father->name);
$this->assertInstanceOf('DateTime', $father->created_at); $this->assertInstanceOf('DateTime', $father->created_at);
$this->assertInstanceOf('DateTime', $father->updated_at); $this->assertInstanceOf('DateTime', $father->updated_at);
...@@ -408,7 +523,7 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -408,7 +523,7 @@ class EmbeddedRelationsTest extends TestCase {
$user->father()->save($father); $user->father()->save($father);
$father->unsetEventDispatcher(); $father->unsetEventDispatcher();
$this->assertNotNull($user->_father); $this->assertNotNull($user->father);
$this->assertEquals('Tom Doe', $user->father->name); $this->assertEquals('Tom Doe', $user->father->name);
$father = new User(array('name' => 'Jim Doe')); $father = new User(array('name' => 'Jim Doe'));
...@@ -422,7 +537,7 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -422,7 +537,7 @@ class EmbeddedRelationsTest extends TestCase {
$father = $user->father()->save($father); $father = $user->father()->save($father);
$father->unsetEventDispatcher(); $father->unsetEventDispatcher();
$this->assertNotNull($user->_father); $this->assertNotNull($user->father);
$this->assertEquals('Jim Doe', $user->father->name); $this->assertEquals('Jim Doe', $user->father->name);
} }
...@@ -437,7 +552,7 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -437,7 +552,7 @@ class EmbeddedRelationsTest extends TestCase {
$father = $user->father()->associate($father); $father = $user->father()->associate($father);
$father->unsetEventDispatcher(); $father->unsetEventDispatcher();
$this->assertNotNull($user->_father); $this->assertNotNull($user->father);
$this->assertEquals('Mark Doe', $user->father->name); $this->assertEquals('Mark Doe', $user->father->name);
} }
...@@ -447,7 +562,6 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -447,7 +562,6 @@ class EmbeddedRelationsTest extends TestCase {
$father = $user->father()->save(new User(array('name' => 'Mark Doe'))); $father = $user->father()->save(new User(array('name' => 'Mark Doe')));
$user->father()->delete(); $user->father()->delete();
$this->assertNull($user->_father);
$this->assertNull($user->father); $this->assertNull($user->father);
} }
...@@ -459,11 +573,180 @@ class EmbeddedRelationsTest extends TestCase { ...@@ -459,11 +573,180 @@ class EmbeddedRelationsTest extends TestCase {
$user->addresses()->save(new Address(array('city' => 'Brussels'))); $user->addresses()->save(new Address(array('city' => 'Brussels')));
$array = $user->toArray(); $array = $user->toArray();
$this->assertArrayNotHasKey('_addresses', $array); $this->assertArrayHasKey('addresses', $array);
$this->assertTrue(is_array($array['addresses']));
}
$user->setExposed(array('_addresses')); public function testEmbeddedSave()
$array = $user->toArray(); {
$this->assertArrayHasKey('_addresses', $array); $user = User::create(array('name' => 'John Doe'));
$address = $user->addresses()->create(array('city' => 'New York'));
$father = $user->father()->create(array('name' => 'Mark Doe'));
$address->city = 'Paris';
$address->save();
$father->name = 'Steve Doe';
$father->save();
$this->assertEquals('Paris', $user->addresses->first()->city);
$this->assertEquals('Steve Doe', $user->father->name);
$user = User::where('name', 'John Doe')->first();
$this->assertEquals('Paris', $user->addresses->first()->city);
$this->assertEquals('Steve Doe', $user->father->name);
$address = $user->addresses()->first();
$father = $user->father;
$address->city = 'Ghent';
$address->save();
$father->name = 'Mark Doe';
$father->save();
$this->assertEquals('Ghent', $user->addresses->first()->city);
$this->assertEquals('Mark Doe', $user->father->name);
$user = User::where('name', 'John Doe')->first();
$this->assertEquals('Ghent', $user->addresses->first()->city);
$this->assertEquals('Mark Doe', $user->father->name);
}
public function testNestedEmbedsOne()
{
$user = User::create(array('name' => 'John Doe'));
$father = $user->father()->create(array('name' => 'Mark Doe'));
$grandfather = $father->father()->create(array('name' => 'Steve Doe'));
$greatgrandfather = $grandfather->father()->create(array('name' => 'Tom Doe'));
$user->name = 'Tim Doe';
$user->save();
$father->name = 'Sven Doe';
$father->save();
$greatgrandfather->name = 'Ron Doe';
$greatgrandfather->save();
$this->assertEquals('Tim Doe', $user->name);
$this->assertEquals('Sven Doe', $user->father->name);
$this->assertEquals('Steve Doe', $user->father->father->name);
$this->assertEquals('Ron Doe', $user->father->father->father->name);
$user = User::where('name', 'Tim Doe')->first();
$this->assertEquals('Tim Doe', $user->name);
$this->assertEquals('Sven Doe', $user->father->name);
$this->assertEquals('Steve Doe', $user->father->father->name);
$this->assertEquals('Ron Doe', $user->father->father->father->name);
}
public function testNestedEmbedsMany()
{
$user = User::create(array('name' => 'John Doe'));
$country1 = $user->addresses()->create(array('country' => 'France'));
$country2 = $user->addresses()->create(array('country' => 'Belgium'));
$city1 = $country1->addresses()->create(array('city' => 'Paris'));
$city2 = $country2->addresses()->create(array('city' => 'Ghent'));
$city3 = $country2->addresses()->create(array('city' => 'Brussels'));
$city3->city = 'Bruges';
$city3->save();
$this->assertEquals(2, $user->addresses()->count());
$this->assertEquals(1, $user->addresses()->first()->addresses()->count());
$this->assertEquals(2, $user->addresses()->last()->addresses()->count());
$user = User::where('name', 'John Doe')->first();
$this->assertEquals(2, $user->addresses()->count());
$this->assertEquals(1, $user->addresses()->first()->addresses()->count());
$this->assertEquals(2, $user->addresses()->last()->addresses()->count());
}
public function testNestedMixedEmbeds()
{
$user = User::create(array('name' => 'John Doe'));
$father = $user->father()->create(array('name' => 'Mark Doe'));
$country1 = $father->addresses()->create(array('country' => 'France'));
$country2 = $father->addresses()->create(array('country' => 'Belgium'));
$country2->country = 'England';
$country2->save();
$father->name = 'Steve Doe';
$father->save();
$this->assertEquals('France', $user->father->addresses()->first()->country);
$this->assertEquals('England', $user->father->addresses()->last()->country);
$this->assertEquals('Steve Doe', $user->father->name);
$user = User::where('name', 'John Doe')->first();
$this->assertEquals('France', $user->father->addresses()->first()->country);
$this->assertEquals('England', $user->father->addresses()->last()->country);
$this->assertEquals('Steve Doe', $user->father->name);
}
public function testDoubleAssociate()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'Paris'));
$user->addresses()->associate($address);
$user->addresses()->associate($address);
$address = $user->addresses()->first();
$user->addresses()->associate($address);
$this->assertEquals(1, $user->addresses()->count());
$user = User::where('name', 'John Doe')->first();
$user->addresses()->associate($address);
$this->assertEquals(1, $user->addresses()->count());
$user->save();
$user->addresses()->associate($address);
$this->assertEquals(1, $user->addresses()->count());
}
public function testSaveEmptyModel()
{
$user = User::create(array('name' => 'John Doe'));
$user->addresses()->save(new Address);
$this->assertNotNull($user->addresses);
$this->assertEquals(1, $user->addresses()->count());
}
public function testIncrementEmbedded()
{
$user = User::create(array('name' => 'John Doe'));
$address = $user->addresses()->create(array('city' => 'New York', 'visited' => 5));
$address->increment('visited');
$this->assertEquals(6, $address->visited);
$this->assertEquals(6, $user->addresses()->first()->visited);
$user = User::where('name', 'John Doe')->first();
$this->assertEquals(6, $user->addresses()->first()->visited);
$user = User::where('name', 'John Doe')->first();
$address = $user->addresses()->first();
$address->decrement('visited');
$this->assertEquals(5, $address->visited);
$this->assertEquals(5, $user->addresses()->first()->visited);
$user = User::where('name', 'John Doe')->first();
$this->assertEquals(5, $user->addresses()->first()->visited);
}
public function testPaginateEmbedsMany()
{
$user = User::create(array('name' => 'John Doe'));
$user->addresses()->save(new Address(array('city' => 'New York')));
$user->addresses()->save(new Address(array('city' => 'Paris')));
$user->addresses()->save(new Address(array('city' => 'Brussels')));
$results = $user->addresses()->paginate(2);
$this->assertEquals(2, $results->count());
$this->assertEquals(3, $results->getTotal());
} }
} }
...@@ -267,29 +267,33 @@ class ModelTest extends TestCase { ...@@ -267,29 +267,33 @@ class ModelTest extends TestCase {
public function testSoftDelete() public function testSoftDelete()
{ {
$user = new Soft; Soft::create(array('name' => 'John Doe'));
$user->name = 'Softy'; Soft::create(array('name' => 'Jane Doe'));
$user->save();
$this->assertEquals(2, Soft::count());
$user = Soft::where('name', 'John Doe')->first();
$this->assertEquals(true, $user->exists); $this->assertEquals(true, $user->exists);
$this->assertEquals(false, $user->trashed());
$this->assertNull($user->deleted_at);
$user->delete(); $user->delete();
$this->assertEquals(true, $user->trashed());
$this->assertNotNull($user->deleted_at);
$check = Soft::find($user->_id); $user = Soft::where('name', 'John Doe')->first();
$this->assertEquals(null, $check); $this->assertNull($user);
$all = Soft::get();
$this->assertEquals(0, $all->count());
$all = Soft::withTrashed()->get(); $this->assertEquals(1, Soft::count());
$this->assertEquals(1, $all->count()); $this->assertEquals(2, Soft::withTrashed()->count());
$check = $all[0]; $user = Soft::withTrashed()->where('name', 'John Doe')->first();
$this->assertInstanceOf('Carbon\Carbon', $check->deleted_at); $this->assertNotNull($user);
$this->assertEquals(true, $check->trashed()); $this->assertInstanceOf('Carbon\Carbon', $user->deleted_at);
$this->assertEquals(true, $user->trashed());
$check->restore(); $user->restore();
$all = Soft::get(); $this->assertEquals(2, Soft::count());
$this->assertEquals(1, $all->count());
} }
public function testPrimaryKey() public function testPrimaryKey()
...@@ -393,6 +397,12 @@ class ModelTest extends TestCase { ...@@ -393,6 +397,12 @@ class ModelTest extends TestCase {
$user = User::create(array('name' => 'Jane Doe', 'birthday' => '2005-08-08')); $user = User::create(array('name' => 'Jane Doe', 'birthday' => '2005-08-08'));
$this->assertInstanceOf('Carbon\Carbon', $user->birthday); $this->assertInstanceOf('Carbon\Carbon', $user->birthday);
$user = User::create(array('name' => 'Jane Doe', 'entry' => array('date' => '2005-08-08')));
$this->assertInstanceOf('Carbon\Carbon', $user->getAttribute('entry.date'));
$user->setAttribute('entry.date', new DateTime);
$this->assertInstanceOf('Carbon\Carbon', $user->getAttribute('entry.date'));
} }
public function testIdAttribute() public function testIdAttribute()
...@@ -406,14 +416,28 @@ class ModelTest extends TestCase { ...@@ -406,14 +416,28 @@ class ModelTest extends TestCase {
public function testPushPull() public function testPushPull()
{ {
$user = User::create(array('name' => 'John Doe', 'tags' => array())); $user = User::create(array('name' => 'John Doe'));
$result = User::where('_id', $user->_id)->push('tags', 'tag1'); $user->push('tags', 'tag1');
$user->push('tags', array('tag1', 'tag2'));
$user->push('tags', 'tag2', true);
$this->assertEquals(array('tag1', 'tag1', 'tag2'), $user->tags);
$user = User::where('_id', $user->_id)->first();
$this->assertEquals(array('tag1', 'tag1', 'tag2'), $user->tags);
$user->pull('tags', 'tag1');
$this->assertEquals(array('tag2'), $user->tags);
$user = User::where('_id', $user->_id)->first(); $user = User::where('_id', $user->_id)->first();
$this->assertEquals(array('tag2'), $user->tags);
$user->push('tags', 'tag3');
$user->pull('tags', array('tag2', 'tag3'));
$this->assertTrue(is_int($result)); $this->assertEquals(array(), $user->tags);
$this->assertTrue(is_array($user->tags)); $user = User::where('_id', $user->_id)->first();
$this->assertEquals(1, count($user->tags)); $this->assertEquals(array(), $user->tags);
} }
public function testRaw() public function testRaw()
......
...@@ -611,4 +611,20 @@ class QueryBuilderTest extends TestCase { ...@@ -611,4 +611,20 @@ class QueryBuilderTest extends TestCase {
$this->assertEquals(1, $user['age']); $this->assertEquals(1, $user['age']);
} }
public function testProjections()
{
DB::collection('items')->insert(array(
array('name' => 'fork', 'tags' => array('sharp', 'pointy')),
array('name' => 'spork', 'tags' => array('sharp', 'pointy', 'round', 'bowl')),
array('name' => 'spoon', 'tags' => array('round', 'bowl')),
));
$results = DB::collection('items')->project(array('tags' => array('$slice' => 1)))->get();
foreach ($results as $result)
{
$this->assertEquals(1, count($result['tags']));
}
}
} }
...@@ -134,7 +134,7 @@ class QueryTest extends TestCase { ...@@ -134,7 +134,7 @@ class QueryTest extends TestCase {
$this->assertEquals(4, count($users)); $this->assertEquals(4, count($users));
$users = User::whereNotNull('age') $users = User::whereNotNull('age')
->whereNotIn('age', array(33, 35))->get(); ->whereNotIn('age', array(33, 35))->get();
$this->assertEquals(3, count($users)); $this->assertEquals(3, count($users));
} }
...@@ -214,31 +214,56 @@ class QueryTest extends TestCase { ...@@ -214,31 +214,56 @@ class QueryTest extends TestCase {
public function testSubquery() public function testSubquery()
{ {
$users = User::where('title', 'admin')->orWhere(function($query) $users = User::where('title', 'admin')->orWhere(function($query)
{ {
$query->where('name', 'Tommy Toe') $query->where('name', 'Tommy Toe')
->orWhere('name', 'Error'); ->orWhere('name', 'Error');
}) })
->get(); ->get();
$this->assertEquals(5, count($users)); $this->assertEquals(5, count($users));
$users = User::where('title', 'user')->where(function($query) $users = User::where('title', 'user')->where(function($query)
{ {
$query->where('age', 35) $query->where('age', 35)
->orWhere('name', 'like', '%harry%'); ->orWhere('name', 'like', '%harry%');
}) })
->get(); ->get();
$this->assertEquals(2, count($users)); $this->assertEquals(2, count($users));
$users = User::where('age', 35)->orWhere(function($query) $users = User::where('age', 35)->orWhere(function($query)
{ {
$query->where('title', 'admin') $query->where('title', 'admin')
->orWhere('name', 'Error'); ->orWhere('name', 'Error');
}) })
->get(); ->get();
$this->assertEquals(5, count($users)); $this->assertEquals(5, count($users));
$users = User::whereNull('deleted_at')
->where('title', 'admin')
->where(function($query)
{
$query->where('age', '>', 15)
->orWhere('name', 'Harry Hoe');
})
->get();
$this->assertEquals(3, $users->count());
$users = User::whereNull('deleted_at')
->where(function($query)
{
$query->where('name', 'Harry Hoe')
->orWhere(function($query)
{
$query->where('age', '>', 15)
->where('title', '<>', 'admin');
});
})
->get();
$this->assertEquals(5, $users->count());
} }
public function testWhereRaw() public function testWhereRaw()
...@@ -280,4 +305,15 @@ class QueryTest extends TestCase { ...@@ -280,4 +305,15 @@ class QueryTest extends TestCase {
$this->assertEquals(2, count($users)); $this->assertEquals(2, count($users));
} }
public function testPaginate()
{
$results = User::paginate(2);
$this->assertEquals(2, $results->count());
$this->assertNotNull($results->first()->title);
$results = User::paginate(2, array('name', 'age'));
$this->assertEquals(2, $results->count());
$this->assertNull($results->first()->title);
}
} }
...@@ -131,6 +131,7 @@ class RelationsTest extends TestCase { ...@@ -131,6 +131,7 @@ class RelationsTest extends TestCase {
$items = $user->items; $items = $user->items;
$this->assertEquals(1, count($items)); $this->assertEquals(1, count($items));
$this->assertInstanceOf('Item', $items[0]); $this->assertInstanceOf('Item', $items[0]);
$this->assertEquals($user->_id, $items[0]->user_id);
// Has one // Has one
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
...@@ -141,6 +142,7 @@ class RelationsTest extends TestCase { ...@@ -141,6 +142,7 @@ class RelationsTest extends TestCase {
$role = $user->role; $role = $user->role;
$this->assertInstanceOf('Role', $role); $this->assertInstanceOf('Role', $role);
$this->assertEquals('admin', $role->type); $this->assertEquals('admin', $role->type);
$this->assertEquals($user->_id, $role->user_id);
} }
public function testBelongsToMany() public function testBelongsToMany()
...@@ -415,6 +417,59 @@ class RelationsTest extends TestCase { ...@@ -415,6 +417,59 @@ class RelationsTest extends TestCase {
$address = $client->addresses->first(); $address = $client->addresses->first();
$this->assertEquals('Paris', $address->data['city']); $this->assertEquals('Paris', $address->data['city']);
$client = Client::with('addresses')->first();
$this->assertEquals('Paris', $client->addresses->first()->data['city']);
}
public function testDoubleSaveOneToMany()
{
$author = User::create(array('name' => 'George R. R. Martin'));
$book = Book::create(array('title' => 'A Game of Thrones'));
$author->books()->save($book);
$author->books()->save($book);
$author->save();
$this->assertEquals(1, $author->books()->count());
$this->assertEquals($author->_id, $book->author_id);
$author = User::where('name', 'George R. R. Martin')->first();
$book = Book::where('title', 'A Game of Thrones')->first();
$this->assertEquals(1, $author->books()->count());
$this->assertEquals($author->_id, $book->author_id);
$author->books()->save($book);
$author->books()->save($book);
$author->save();
$this->assertEquals(1, $author->books()->count());
$this->assertEquals($author->_id, $book->author_id);
}
public function testDoubleSaveManyToMany()
{
$user = User::create(array('name' => 'John Doe'));
$client = Client::create(array('name' => 'Admins'));
$user->clients()->save($client);
$user->clients()->save($client);
$user->save();
$this->assertEquals(1, $user->clients()->count());
$this->assertEquals(array($user->_id), $client->user_ids);
$this->assertEquals(array($client->_id), $user->client_ids);
$user = User::where('name', 'John Doe')->first();
$client = Client::where('name', 'Admins')->first();
$this->assertEquals(1, $user->clients()->count());
$this->assertEquals(array($user->_id), $client->user_ids);
$this->assertEquals(array($client->_id), $user->client_ids);
$user->clients()->save($client);
$user->clients()->save($client);
$user->save();
$this->assertEquals(1, $user->clients()->count());
$this->assertEquals(array($user->_id), $client->user_ids);
$this->assertEquals(array($client->_id), $user->client_ids);
} }
} }
...@@ -9,7 +9,10 @@ class TestCase extends Orchestra\Testbench\TestCase { ...@@ -9,7 +9,10 @@ class TestCase extends Orchestra\Testbench\TestCase {
*/ */
protected function getPackageProviders() protected function getPackageProviders()
{ {
return array('Jenssegers\Mongodb\MongodbServiceProvider'); return array(
'Jenssegers\Mongodb\MongodbServiceProvider',
'Jenssegers\Mongodb\Auth\ReminderServiceProvider',
);
} }
/** /**
......
...@@ -6,4 +6,9 @@ class Address extends Eloquent { ...@@ -6,4 +6,9 @@ class Address extends Eloquent {
protected static $unguarded = true; protected static $unguarded = true;
public function addresses()
{
return $this->embedsMany('Address');
}
} }
...@@ -19,6 +19,6 @@ class Client extends Eloquent { ...@@ -19,6 +19,6 @@ class Client extends Eloquent {
public function addresses() public function addresses()
{ {
return $this->hasMany('Address', 'data.client_id', 'data.address_id'); return $this->hasMany('Address', 'data.address_id', 'data.client_id');
} }
} }
...@@ -5,9 +5,10 @@ use Jenssegers\Mongodb\Eloquent\SoftDeletingTrait; ...@@ -5,9 +5,10 @@ use Jenssegers\Mongodb\Eloquent\SoftDeletingTrait;
class Soft extends Eloquent { class Soft extends Eloquent {
use SoftDeletingTrait; use SoftDeletingTrait;
protected $collection = 'soft'; protected $collection = 'soft';
protected $dates = array('deleted_at'); protected static $unguarded = true;
protected $dates = array('deleted_at');
} }
...@@ -2,12 +2,16 @@ ...@@ -2,12 +2,16 @@
use Jenssegers\Mongodb\Model as Eloquent; use Jenssegers\Mongodb\Model as Eloquent;
use Illuminate\Auth\UserTrait;
use Illuminate\Auth\UserInterface; use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableTrait;
use Illuminate\Auth\Reminders\RemindableInterface; use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface { class User extends Eloquent implements UserInterface, RemindableInterface {
protected $dates = array('birthday'); use UserTrait, RemindableTrait;
protected $dates = array('birthday', 'entry.date');
protected static $unguarded = true; protected static $unguarded = true;
public function books() public function books()
...@@ -60,66 +64,6 @@ class User extends Eloquent implements UserInterface, RemindableInterface { ...@@ -60,66 +64,6 @@ class User extends Eloquent implements UserInterface, RemindableInterface {
return $this->embedsOne('User'); return $this->embedsOne('User');
} }
/**
* Get the unique identifier for the user.
*
* @return mixed
*/
public function getAuthIdentifier()
{
return $this->getKey();
}
/**
* Get the password for the user.
*
* @return string
*/
public function getAuthPassword()
{
return $this->password;
}
/**
* Get the e-mail address where password reminders are sent.
*
* @return string
*/
public function getReminderEmail()
{
return $this->email;
}
/**
* Get the token value for the "remember me" session.
*
* @return string
*/
public function getRememberToken()
{
return $this->rememberToken;
}
/**
* Set the token value for the "remember me" session.
*
* @param string $value
* @return void
*/
public function setRememberToken($value)
{
$this->rememberToken = $value;
}
/**
* Get the column name for the "remember me" token.
*
* @return string
*/
public function getRememberTokenName() {
return 'remember_token';
}
protected function getDateFormat() protected function getDateFormat()
{ {
return 'l jS \of F Y h:i:s A'; return 'l jS \of F Y h:i:s A';
......
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