Touching Eloquent model via polymorphic relationship - php

Laravel 5.7. I have two models, Foo and Content. Their relationship is polymorphic because Content can also be related to other models:
class Foo extends Model
{
public function contents()
{
return $this->morphToMany('App\Content');
}
}
class Content extends Model
{
use LastModified;
public function foos()
{
return $this->morphedByMany('App\Foo');
}
}
I want to touch the Foo model whenever the Content model is updated. So I use a LastModified trait for the Content model:
trait LastModified
{
protected static function bootLastModified()
{
static::updating(function ($model) {
static::updateLastModified($model);
});
}
protected static function updateLastModified($model)
{
$foos = $model->foos;
if (count($foos) > 0) {
foreach ($foos as $foo) {
$foo->touch();
}
}
}
}
My problem is that $model->foos returns the correct Foo models, but with the wrong ids. Instead of the Foo's own model id, each Foo has the id of the pivot table row. This means that the wrong Foo row is touched.

Laravel has built-in functionality for touching parent timestamps.
On the content model, you can add a property telling which relationships should be touched when the given model is updated.
The following should work:
class Content extends Model
{
protected $touches = ['foos'];
public function foos()
{
return $this->morphedByMany('App\Foo');
}
}
Edit: Since you are using a static updated event you should manually call $model->touchOwners() from the static::updated

Related

Laravel One to One Polymorphic Relation

There are 3 models and their respective tables
1.Customer - id, name
2.Address - id, name
3.ExternalMapping - model_id, model_type, external_id
How do I define the relation in Customer model that will give me the external_id in ExternalMapping model?
I tried this but it didn't work
class Customer extends Model
{
public function cascades()
{
return $this->morphMany('App\ExternalMapping', 'external');
}
}
class ExternalMapping extends Model
{
public function external()
{
return $this->morphTo();
}
}
You need one to one polymorphic relation. Not one to many relation. So do this.
class Customer extends Model
{
public function cascade()
{
return $this->morphOne('App\ExternalMapping', 'external');
}
}
class ExternalMapping extends Model
{
public function external()
{
return $this->morphTo();
}
}
print_r($customer->cascade->external_id;)
The column you're referencing on your Customer class is incorrect, you should be referencing the model column not the external column as you currently are:
class Customer extends Model
{
public function cascades()
{
return $this->morphMany('App\ExternalMapping', 'model');
}
}
That will tell Laravel to look for a column on your ExternalMapping table that has the name model_id with the id value of the current Customer.
Then to grab the external_id:
$customer->cascades->external_id;

Laravel 5 - passing variable to relationship model

If I have below models:
class User extends Model{
protected $someDataFromExt = ['taskID0' => 'test', 'taskID1' => 'ting'];
public function tasks() { return $this->hasMany('Task'); }
}
class Task extends Model{
protected $appends = ['ext_data'];
public function user() { return $this->belongsTo('User'); }
public function getExtDataAttribute(){ return $this->external_data; }
}
I would like, when I do: $tasks = auth()->user()->tasks->all(); I want to pass $user->someDataFromExt (based on task ID) to task model, so I later in my $tasks variable I can access:
foreach($tasks as $task){
echo $task->ext_data;
}
Which will return data that was given from user model earlier?
Is this possible? how?
Not completely following why the task data is being stored on the user model, but I think what you’re asking can be achieved via your accessor method on your task model, providing you make the someDataFromExt public:
<?php
class Task extends Model {
public function getExtDataAttribute()
{
return $this->user->someDataFromExt['taskID' . $this->id];
}
}

Laravel DRY nested eager loading

Suppose I got a model with a few relations:
class Author extends Model
{
public function shortStories()
{
return $this->hasMany(ShortStory::class);
}
public function essays()
{
return $this->hasMany(Essay::class);
}
public function books()
{
return $this->hasMany(Book::class);
}
}
Now suppose I got two more models that want to eager load this model with its relations:
class Publisher extends Model
{
public function scopeWithAuthor($query)
{
$query->with('author.shortStories', 'author.essays', 'author.books');
}
}
class Reviewer extends Model
{
public function scopeWithAuthor($query)
{
$query->with('author.shortStories', 'author.essays', 'author.books');
}
}
Problem - if the relations of author change I now need to reflect this in multiple locations.
My question - how do I achieve this in DRY style?
I know that I could add a protected $with to the Author class but then it would always load the relation, not just when required.
With that in mind, one solution I have come up with is to extend the Author model like so:
class AuthorWithRelations extends Author
{
protected $with = ['shortStories', 'essays', 'books'];
}
This then allows me to refactor the scopes of the other models like so:
class Publisher extends Model
{
public function scopeWithAuthor($query)
{
$query->with('authorWithRelations');
}
}
class Reviewer extends Model
{
public function scopeWithAuthor($query)
{
$query->with('authorWithRelations');
}
}
This works well enough but really I was wondering if Laravel provides a better / inbuilt approach to this?

information about related models in Laravel

Hello I´m writing an API and I want to display more information about the related model.
Routes.php
Route::resource('makes', 'MakesController');
MakesController.php
class MakesController extends Controller
{
public function index()
{
$data = Make::all();
return response()->json($data);
}
}
This returns only information about the makes (id, name)
but how can I display also how many models has each make?
I have defined these two models
class Make extends Model
{
public function models()
{
return $this->hasMany('App\CarModel');
}
}
class CarModel extends Model
{
public function make()
{
return $this->belongsTo('App\Make');
}
}
You can define $visible field in the Make model's class like this:
protected $visible = ['models'];
This will automatically appends the related model's array to array/json.
You can also use an optional way with makeVisible method:
class MakesController extends Controller
{
public function index()
{
$data = Make::all();
return response()->makeVisible('models')->json($data);
}
}

Laravel 4, Retrive morphed Parent Model, querying child

I didn't know how to ask the question so, the question is not as clear as the description. But here it goes.
I want to query the parent Model, by quering the child Model. Here is the basic Models:
//Gig.php
class Gig extends Eloquent
{
public function gigable()
{
return $this->morphTo('profile');
}
}
//Band.php
class Band extends Eloquent {
public function gigs()
{
return $this->morphMany('Gig','profile');
}
}
//Musician.php
class Musician extends Eloquent {
public function gigs()
{
return $this->morphMany('Gig','profile');
}
}
Now, I know I can do something like this:
Gig::find(1)->gigable
Is there a way that I can retrieve gigables in one query
and for example for my api I can return all the gigable at once.
Something like:
Gig::where('id','>',1)->gigable
I know thats doesn't work.
Such as in reverse of this I can do something like,
Band::with('gigs')->where('created_at','<',Carbon::now())->get();
Use as follows
//Gig.php
class Gig extends Eloquent {
public function gigable()
{
return $this->morphTo();
}
}
//Band.php
class Band extends Eloquent {
public function gigs()
{
return $this->morphMany('Gig','gigable');
}
}
//Musician.php
class Musician extends Eloquent {
public function gigs()
{
return $this->morphMany('Gig','gigable');
}
}
Then you will be able to get the gigs this way:
$band = Band::with('gigs')->where('created_at','<',Carbon::now())->get();
$gigs = $band->gigs;
If this is what you wanted... your question is unclear

Categories