I have two models, one embed in the other likes this:
class Media extends Moloquent {
public function user()
{
return $this->belongsTo('User');
}
public function comments()
{
return $this->embedsMany('Comment');
}
public function video()
{
return $this->embedsOne('Video');
}
}
class Video extends Moloquent {
...
}
And I want to get a Media with the Video embedded with some getters mutators.
$media = Media::with(['comments', 'user', 'video'])->find($id);
The load of comments and user is working great, but when I add the video loading it crashes showing the next error:
[2015-01-20 09:50:42] alpha.ERROR: exception 'ErrorException' with message 'Argument 2 passed to Jenssegers\Mongodb\Relations\EmbedsOneOrMany::match() must be an instance of Illuminate\Database\Eloquent\Collection, null given, called in /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php on line 478 and defined' in /home/vagrant/sites/media/vendor/jenssegers/mongodb/src/Jenssegers/Mongodb/Relations/EmbedsOneOrMany.php:112
Stack trace:
#0 /home/vagrant/sites/media/vendor/jenssegers/mongodb/src/Jenssegers/Mongodb/Relations/EmbedsOneOrMany.php(112): Illuminate\Exception\Handler->handleError(4096, 'Argument 2 pass...', '/home/vagrant/s...', 112, Array)
#1 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(478): Jenssegers\Mongodb\Relations\EmbedsOneOrMany->match(Array, NULL, 'video')
#2 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(445): Illuminate\Database\Eloquent\Builder->loadRelation(Array, 'video', Object(Closure))
#3 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(158): Illuminate\Database\Eloquent\Builder->eagerLoadRelations(Array)
#4 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(125): Illuminate\Database\Eloquent\Builder->get(Array)
#5 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(82): Illuminate\Database\Eloquent\Builder->first(Array)
#6 /home/vagrant/sites/media/app/controllers/Api2Controller.php(41): Illuminate\Database\Eloquent\Builder->find('54bcf9dc0390693...')
#7 [internal function]: Api2Controller->getMedia('54bcf9dc0390693...')
#8 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(231): call_user_func_array(Array, Array)
#9 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(93): Illuminate\Routing\Controller->callAction('getMedia', Array)
#10 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(62): Illuminate\Routing\ControllerDispatcher->call(Object(Api2Controller), Object(Illuminate\Routing\Route), 'getMedia')
#11 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Routing/Router.php(962): Illuminate\Routing\ControllerDispatcher->dispatch(Object(Illuminate\Routing\Route), Object(Illuminate\Http\Request), 'Api2Controller', 'getMedia')
#12 [internal function]: Illuminate\Routing\Router->Illuminate\Routing\{closure}('54bcf9dc0390693...')
#13 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Routing/Route.php(109): call_user_func_array(Object(Closure), Array)
#14 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Routing/Router.php(1028): Illuminate\Routing\Route->run(Object(Illuminate\Http\Request))
#15 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Routing/Router.php(996): Illuminate\Routing\Router->dispatchToRoute(Object(Illuminate\Http\Request))
#16 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(775): Illuminate\Routing\Router->dispatch(Object(Illuminate\Http\Request))
#17 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(745): Illuminate\Foundation\Application->dispatch(Object(Illuminate\Http\Request))
#18 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Session/Middleware.php(72): Illuminate\Foundation\Application->handle(Object(Illuminate\Http\Request), 1, true)
#19 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Cookie/Queue.php(47): Illuminate\Session\Middleware->handle(Object(Illuminate\Http\Request), 1, true)
#20 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Cookie/Guard.php(51): Illuminate\Cookie\Queue->handle(Object(Illuminate\Http\Request), 1, true)
#21 /home/vagrant/sites/media/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Illuminate\Cookie\Guard->handle(Object(Illuminate\Http\Request), 1, true)
#22 /home/vagrant/sites/media/vendor/laravel/framework/src/Illuminate/Foundation/Application.php(641): Stack\StackedHttpKernel->handle(Object(Illuminate\Http\Request))
#23 /home/vagrant/sites/media/public/index.php(73): Illuminate\Foundation\Application->run()
#24 {main} [] []
I guess that the eager loading is only allowing Collections and no individual Models?
Thanks for any help!
Eager loading embedded relations does not really make that much sense, as they are already embedded in the object you get back from mongodb.
However, the syntax should still work, even if it does nothing. I'll try to fix this in a next release.
Thanks for your answer. I agree with you, but I have some appends attributes in the embedded model, and I think that without the eager loading it doesn't work (doesn't return the appended attributes). Because of this I was trying to use the eager loading.
Hi Jens, interesting.
In extension to the question of juanilton1. It suggests too the embedding of the reference, such as:
[{
"_id" : ObjectId("54f9ce74e75a4fe067b7acd9"),
"first_name" : "Hostan",
"last_name" : "Placstac",
"sex" : "male",
"agree_with_conditions" : true,
"updated_at" : ISODate("2015-03-06T15:59:17.915Z"),
"created_at" : ISODate("2015-03-06T15:57:40.700Z"),
"password" : "$2y$10$LrAW.6EQT2KZFYfG996jhuGGCxVPb0PrWN6nIF3M/VP21U2djpVFe",
"active" : true,
"remember_token" : "zjUXrQfxyngCRQrDhxlZ6Hz0HyC39GtljeFaNvmb6AMvUFsXFWRCck0FMcR6",
"emails" : [{
"_id": ObjectId("54f9ce74e75a4fe067b7acda")
}]
}]
[{
"_id" : ObjectId("54f9ce74e75a4fe067b7acda"),
"email" : "[email protected]",
"active" : true,
"updated_at" : ISODate("2015-03-06T15:58:19.744Z"),
"created_at" : ISODate("2015-03-06T15:57:40.708Z")
}]
I'm using emails in difference situations. That's why I store the emails in own collection.
In my User model, i've embedsMany to Email. How do I eager load User with Emails?
Wouldn't this be a belongsToMany relationship the way you have it set up?
I would think you would want to change it to where the user ID is stored with the email, so that from the User view it is a hasMany relationship, and from the email a belongsTo relationship. Assuming that the email would be unique to one person.
Hi Mike,
Thanks for your reply. I'm using emails in a lot of contexts. That's why I've a email collection (instead of data duplication).
I don't want to use link tables, but embedded arrays with id's (see my example above).
In my User model, I've:
public function emails() {
return $this->hasMany('App\Email');
}
In my Email model, I've:
public function user() {
return $this->belongsTo('App\User');
}
But no result by storing embedded relations. Even so if I change hasMany into embedsMany....
i realised you need to set the _foreign_key_ on the relationships. Something like
public function user() {
return $this->belongsTo('App\User', 'user_id');
}
works for me
Hi Murwa,
Thanks, but your solution only works for me by storing the user_id in de email collection.
I'm really confused about the embedded relation:
I only want to embed the reference to another collection instead of using a linking table.
@Henri85 look how the many to many relationship work : https://github.com/jenssegers/laravel-mongodb#relations. Try it.
Also, you can build a kind of custom relationship by defining a custom schema and custom getters and setters like this ones :
class User {
public function getEmailsAttribute();
public function attachEmail($email, $pivot = array());
public function removeEmail($emailId);
}
@Alexandre, thanks, and yes, I know your suggestions.
I'm wondering why embedded references to other collections is not default
supported. Now, I'm obliged to save emails in difference (sub)collections.
The second option is using linking tables. The third option is to create a
custom relationship.
I consider to downshift MySQL.
2015-03-09 18:40 GMT+01:00 Alexandre Butynski [email protected]:
@Henri85 https://github.com/Henri85 look how the many to many
relationship work :
https://github.com/jenssegers/laravel-mongodb#relations. Try it.Also, you can build a kind of custom relationship by defining a custom
schema and custom getters and setters like this ones :class User { public function getEmailsAttribute(); public function attachEmail($email, $pivot = array()); public function removeEmail($emailId);}
—
Reply to this email directly or view it on GitHub
https://github.com/jenssegers/laravel-mongodb/issues/397#issuecomment-77903719
.
I just wanted to say, that i have the exact problem with embedOne as @juaniiton1 .
When I use "with" I get "Argument 2..." Exception.
My intention is about GeoJSON Point, as I _have_ to store GeoJSON into mongoDB, but i'd still like to export it as [lng,lat] only. i would like to override toArray() method (or some appended attributes)
for embedOne problem i think i solved it already so ill share my fix
in the file \Jenssegers\Mongodb\Relation\EmbedOne.php
add the function i write below to the file EmbedOne.php
then you will be able to use eagerLoading
class EmbedsOne extends EmbedsOneOrMany {
/**
* Shorthand to get the results of the relationship.
*
* @return Jenssegers\Mongodb\Eloquent\Collection
*/
public function get()
{
return new Collection($this->getResults());
}
/**
* Get the embedded records array.
* dn additional because null will failed on match function
* @return array
*/
protected function getEmbedded()
{
return parent::getEmbedded() ?: [];
}
}
but to be able use silent save feature please see issue #488 i explain how to fix it too
Is there an accepted fix for this? I understand that the embedsOne relationship is directly included on the original object, but I've found that in the latest Lumen that without eager loading the relationship on the model, you end up with missing keys...e.g.
{
"_id": "574cfeb79a89200be60018b3",
"updated_at": "2016-05-31 03:02:15",
"created_at": "2016-05-31 03:02:15",
"embededDocument": {
"updated_at": {},
"created_at": {},
"_id": {}
},
@jenssegers is this intended functionality?
checking in to see if there's a fix or workaround for this? though eager loading shouldn't strictly be necessary, it prevents you from getting the ID of deeply nested items.
A concrete example: if you have Content -> embedsOne Video -> embedsMany Captions, the _id property for each Caption will be an empty object as shown in the comment by @damienwebdev when retrieving the document.
My current workaround is to manually load the relationship i.e.
$content = Content::find($id);
$content->video->load('captions');
This works in the short run for me
I have a crazy solution for this problem :D
$media = Media::first();
// load video relation
$media->video = $media->video;
return $media;
I solved my problem using something along the lines suggested by @sakirsensoy , but I overrode the toArray() method so it looks like this:
public function toArray() {
$this->setAttribute('community', $this->getAttribute('community'));
return parent::toArray();
}
Most helpful comment
Is there an accepted fix for this? I understand that the embedsOne relationship is directly included on the original object, but I've found that in the latest Lumen that without eager loading the relationship on the model, you end up with missing keys...e.g.
@jenssegers is this intended functionality?