Questions related to Laravel Eloquent and Namespacing

I am creating a CMS package for Laravel .

All my models in this package are bound and enabled from the IoC container so that they can easily be overwritten in any single package deployment.

For non-polymorphic relationships, this worked.

For example, Page has many PageModules , so its relationship has changed from:

// \Angel\Core\Page public function modules() { return $this->hasMany('PageModule'); } 

in

 // \Angel\Core\Page public function modules() { return $this->hasMany(App::make('PageModule')); } 

But I was not able to figure out how to do the same with polymorphic relationships.

For example, menus contain MenuItems , and each MenuItem can be tied to one other model, such as a page or BlogPost.

To achieve this Laravel path, I added the following to MenuItem:

 // \Angel\Core\MenuItem public function linkable() { return $this->morphTo(); } 

And this is the relation to LinkableModel , which applies to all models, such as Page and BlogPost:

 // \Angel\Core\LinkableModel public function menuItem() { return $this->morphOne(App::make('MenuItem'), 'linkable'); } 

And the menus_items table (which MenuItems use) has the following rows:

 linkable_type | linkable_id -------------------|-------------- \Angel\Core\Page | 11 \Angel\Core\Page | 4 

This works fine, but I need linkable_type say β€œPage” instead of β€œ\ Angel \ Core \ Page” and be allowed on the β€œIoC” page instead of being hardcoded for a particular class namespace.

What I tried:

According to this question , this should be as simple as defining the $morphClass property for linkable () classes, for example:

 // \Angel\Core\Page protected $morphClass = 'Page'; 

But when I apply this and modify the menus_items table to look like this:

 linkable_type | linkable_id ---------------|-------------- Page | 11 Page | 4 

... I just get a Class 'Page' not found. error Class 'Page' not found. whenever linkable () is called in MenuItem.

This is the exact line in Eloquent that throws an error.

So, I burst into Eloquent and thought I could get away with something like this:

 // \Angel\Core\MenuItem public function linkable() { return $this->morphTo(null, App::make($this->linkable_type)); } 

... this is so close, but alas: Eloquent calls linkable () before filling in the remaining attributes / columns of MenuItem, so $ this-> linkable_type is null and therefore cannot solve anything from IoC.

Thank you so much for any guidance you may have!

+6
source share
1 answer
 public function linkable() { return $this->morphTo(null, App::make($this->linkable_type)); } 

In any case, this will not work, because morphTo() in Illuminate \ Database \ Eloquent \ Model expects

  • polymorphic relation name ('linkable')
  • the type of object to be modified ('Page', not an instance of Page )
  • id of the object to be changed

If not provided, Laravel is smart enough to guess them, and then return the Illuminate\Database\Eloquent\MorphTo .

In addition, $this->linkable_type and $this->linkable_id should not actually be null in this context.

Let me take a quick look at the corresponding part of the morphTo() function:

 $instance = new $class; return new MorphTo( $instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name ); 

Note This is the code from version 4.2.6, the code associated with it seems to be a later version and is slightly different, and the function returns a BelongsTo instead of a MorphTo object.

The problem is precisely in $instance = new $class; - The class is simply instantiated and not allowed. But you can just grab this piece of magic and deal with it yourself:

 public function linkable() { $instance = App::make($this->linkable_type); $id = 'linkable_id'; $type = 'linkable_type'; $name = 'linkable'; return new MorphTo( $instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name ); } 

This should work (not tested it), but I'm not sure about any side effects that may occur in some cases.

Or maybe you could just override the entire function in your MenuItem model and just adjust the corresponding part:

 public function morphTo($name = null, $type = null, $id = null) { if (is_null($name)) { list(, $caller) = debug_backtrace(false); $name = snake_case($caller['function']); } list($type, $id) = $this->getMorphs($name, $type, $id); //eager loading if (is_null($class = $this->$type)) { return new MorphTo( $this->newQuery(), $this, $id, null, $type, $name ); } // normal lazy loading else { // this is the changed part $instance = \App::make($class); // new $class; return new MorphTo( $instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name ); } } 

Note This works well for lazy loading, but does not work for high loading. The problem was raised by looking for a solution to download here.

Now the ratio will be as usual:

 public function linkable() { return $this->morphTo(); } 
+4
source

Source: https://habr.com/ru/post/972119/


All Articles