How to update 'updated_at' of model when updating a pivot table - php

I'm using laravel and I have a many to many relation between products and orders. There is a pivot table called order-product which has additional information that is updated from time to time. I would like to update the 'updated_at' feild in the order table when the corresponding row in the pivot table is updated or for example if a new product is added.
Is that possible?
The relation between a products and an order is belongsToMany()
Edit: here is some of the code
class Order extends Model
{
protected $table = 'order';
protected $dates = ['updated_at'];
public function products()
{
return $this->belongsToMany(Product::class, 'order_product');
}
}
When I want to remove products its by calling
$order->products()->detach()
So where do I put the $touches array thats mentioned in the laravel docs here
I've tried adding it to the product model but its not working.

You can use the Touching Parent Timestamps, here is an example (from laravel docs).
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
/**
* All of the relationships to be touched.
*
* #var array
*/
protected $touches = ['post'];
/**
* Get the post that the comment belongs to.
*/
public function post()
{
return $this->belongsTo('App\Post');
}
}

Related

Get column value from Many to Many Relationship in Laravel

This is my tables structure:
Attribute.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Attribute extends Model
{
protected $guarded = [];
public function products()
{
return $this->belongsToMany('App\Product');
}
}
Product.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $guarded = [];
public function attributes()
{
return $this->belongsToMany('App\Attribute');
}
}
I want to get the value column for each row.
What code should I write in my controller to access this value?
Laravel version: 6.9.0
Thanks
You can solve this problem by adding the following method of your end of the relationship
withPivot(['value']);
public function attributes()
{
return $this->belongsToMany('App\Attribute')->withPivot(['value']);
}
And also
public function products()
{
return $this->belongsToMany('App\Product')->withPivot(['value']);
}
When we implements Many To Many relationship,it default create a intermediate table
In your case that table is attribute_product table, we might reference this table as Pivot
table.
This tables value was retrieve by those model by pivot attribute name as follows:
$product = App\Product::find(1);
foreach ($product->attributes as $attribute) {
echo $attribute->pivot->product_id;
}
To add Extra column in (Pivot table)
By default, only the model keys [$attribute_id,$product_id] will be present on the attribute_product table. If your pivot table contains extra attributes, you must specify them when defining the relationship:
return $this->belongsToMany('App\Attribute')->withPivot('column1', 'column2','value');
To change pivot Attribute Name to your given name
you may wish to rename your intermediate table accessor to values instead of pivot.
return $this->belongsToMany('App\Attribute')
->as('values')
Then you will retrieve by $attribute->values->product_id instead of $attribute->pivot->product_id

Laravel polymorphic-relationship many to many

I'm having some trouble figuring out the polymorphic relationships.
I've read the documentation but for me it is quite confusing.
Hope anyone has the time to help me a bit to understanding it.
What I'm trying to do is to have a very simple tag system for some wallpapers.
I started a new test project just to get this working.
I have 3 models: Wallpaper, Tag and WallpaperTag
class Wallpaper extends Model
{
protected $primaryKey = 'wallpaper_id';
protected $table = 'wallpapers';
protected $guarded = ['wallpaper_id'];
/**
* Get all the tags assigned to this wallpaper
*/
public function tags()
{
//
}
}
class Tag extends Model
{
protected $primaryKey = 'tag_id';
protected $table = 'tags';
protected $guarded = ['tag_id'];
/**
* Get all wallpapers that have this given tag
*/
public function wallpapers()
{
//
}
}
class WallpaperTag extends Model
{
protected $primaryKey = 'wallpaper_tag_id';
protected $table = 'wallpaper_tags';
protected $guarded = ['wallpaper_tag_id'];
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
* Wallpaper relation
*/
public function wallpaper()
{
return $this->belongsTo('App\Wallpaper','wallpaper_id');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
* Tag relation
*/
public function tag()
{
return $this->belongsTo('App\Tag','tag_id');
}
}
The wallpapers table in this test project contains only wallpaper_id
The tags table contanis a tag_id and a tag
The wallpaper_tags table contains a foreign key for both tags.tag_id and wallpapers.wallpaper_id
I've set it up like this so wallpapers can share tags without duplicating them. The problem is that I really dont understand the polymorphic relations and the example in the documentation.
Can anyone here 'spoonfeed' how this would work? :') Thanks in advance for all help.
So you are trying to create a relationship with ManyToMany between 2 tables, which in the DB needs a 3rd table to allow you to create such relationship.
This is due to the fact that one Wallpaper can have many Tag and vice versa! For such you need a 3rd table that holds that information accordingly.
The 3rd table is only holding ids in relationship to your 2 main tables. This allows the flexibility you are looking for, while your Object tables can actually hold information specific to them, without you having to duplicate it.
If you were to store the relationship ids on both tables you would be forced to duplicate your data and that is just something you do not wish on databases! Imagine having to update 1000 rows because it is basically the same wallpaper but with so many different tags.
Anyway, below is the code that should be get you going:
You do need to create a class to represent your relationship table (Kudos on the WallpaperTag class! That is the one!);
You do not touch that class anymore, do not add belongs or any other function!
You create the relationships on the main classes Wallpaper and Tag;
class Wallpaper extends Model
{
...
public function tags()
{
return $this->belongsToMany('App\Tag', 'wallpaper_tag', 'tag_id', 'wallpaper_id');
}
}
class Tag extends Model
{
...
public function wallpapers()
{
return $this->belongsToMany('App\Wallpaper', 'wallpaper_tag', 'wallpaper_id', 'tag_id');
}
}
class WallpaperTag extends Model
{
}
Laravel should create a relationship between your classes and map it accordingly to the correct 3rd table to sort the search for you.
If you follow the semantics all you needed was the class name. If ids are to change, then you will need to start telling Laravel what id column names it should be looking for as you deviate from the normal behaviour. It still finds it, just needs some guidance on the names! Hence why we start adding more parameters to the relationships belongsTo or hasMany etc :)
Pivot Table Migration
You do not need an id for your pivot table since your primary key is a combination of the two foreign keys from the other tables.
$table->bigInteger('wallpaper_id')->unsigned()->nullable();
$table->foreign('wallpaper_id')->references('wallpaper_id')
->on('wallpaper')->onDelete('cascade');
$table->bigInteger('tag_id')->unsigned()->nullable();
$table->foreign('tag_id')->references('tag_id')
->on('tags')->onDelete('cascade');
Let me know if it helped! :3

Polymorphic relationshoip in Laravel

I'm trying to understand polymorphic relationship in Laravel. I know how it works in principle, but the choice of wording in Laravel is not intuitive in this part. Given the exanple,
namespace App;
use Illuminate\Database\Eloquent\Model;
class Like extends Model
{
/**
* Get all of the owning likeable models.
*/
public function likeable()
{
return $this->morphTo();
}
}
class Post extends Model
{
/**
* Get all of the product's likes.
*/
public function likes()
{
return $this->morphMany('App\Like', 'likeable');
}
}
class Comment extends Model
{
/**
* Get all of the comment's likes.
*/
public function likes()
{
return $this->morphMany('App\Like', 'likeable');
}
}
How do yo put in plain English sentence morphTo for instance? It is "belongsto"? and morphmany, hasMany? going further,
$post = App\Post::find(1);
foreach ($post->likes as $like) {
//
}
$likeable = $like->likeable;
morphToMany and morphByMany
How do you describe in plain english?
A polymorphic relationship means an object can have a relationship to more than one type of object. This is determined by two fields in the database rather the typical one foreign key field you would normally see.
Using the code you included in your question any type of object extending the Model class can have a relationship with a Like object. So you could have Comments and Posts that can have Likes associated to them. In your likes table you may have rows where 'likable_type' = 'post' and 'likable_id' = 1 or 'likable_type' = 'comment' and 'likable_id' = 4 for example.

Laravel Removing Pivot data in many to many relationship

Not sure if I set this up correctly. In Laravel I'm creating two models with a many-to-may relationship
The models are Item and Tags. Each one contains a belongsTo to the other.
When I run a query like so:
Item::with('tags')->get();
It returns the collection of items, with each item containing a tags collection. However the each tag in the collection also contains pivot data which I don't need. Here it is in json format:
[{
"id":"49",
"slug":"test",
"order":"0","tags":[
{"id":"3","name":"Blah","pivot":{"item_id":"49","tag_id":"3"}},
{"id":"13","name":"Moo","pivot":{"item_id":"49","tag_id":"13"}}
]
}]
Is there anyway to prevent this data from getting at
you can just add the name of the field in the hidden part in your model like this:
protected $hidden = ['pivot'];
that's it , it works fine with me.
You have asked and you shall receive your answer. But first a few words to sum up the comment section. I personally don't know why you would want / need to do this. I understand if you want to hide it from the output but not selecting it from the DB really has no real benefit. Sure, less data will be transferred and the DB server has a tiny tiny bit less work to do, but you won't notice that in any way.
However it is possible. It's not very pretty though, since you have to override the belongsToMany class.
First, the new relation class:
class BelongsToManyPivotless extends BelongsToMany {
/**
* Hydrate the pivot table relationship on the models.
*
* #param array $models
* #return void
*/
protected function hydratePivotRelation(array $models)
{
// do nothing
}
/**
* Get the pivot columns for the relation.
*
* #return array
*/
protected function getAliasedPivotColumns()
{
return array();
}
}
As you can see this class is overriding two methods. hydratePivotRelation would normally create the pivot model and fill it with data. getAliasedPivotColumns would return an array of all columns to select from the pivot table.
Now we need to get this integrated into our model. I suggest you use a BaseModel class for this but it also works in the model directly.
class BaseModel extends Eloquent {
public function belongsToManyPivotless($related, $table = null, $foreignKey = null, $otherKey = null, $relation = null){
if (is_null($relation))
{
$relation = $this->getBelongsToManyCaller();
}
$foreignKey = $foreignKey ?: $this->getForeignKey();
$instance = new $related;
$otherKey = $otherKey ?: $instance->getForeignKey();
if (is_null($table))
{
$table = $this->joiningTable($related);
}
$query = $instance->newQuery();
return new BelongsToManyPivotless($query, $this, $table, $foreignKey, $otherKey, $relation);
}
}
I edited the comments out for brevity but otherwise the method is just like belongsToMany from Illuminate\Database\Eloquent\Model. Of course except the relation class that gets created. Here we use our own BelongsToManyPivotless.
And finally, this is how you use it:
class Item extends BaseModel {
public function tags(){
return $this->belongsToManyPivotless('Tag');
}
}
If you want to remove pivot data then you can use as protected $hidden = ['pivot']; #Amine_Dev suggested, so i have used it but it was not working for me,
but the problem really was that i was using it in wrong model so i want to give more detail in it that where to use it, so you guys don't struggle with the problem which i have struggled.
So if you are fetching the data as :
Item::with('tags')->get();
then you have to assign pivot to hidden array like below
But keep in mind that you have to define it in Tag model not in Item model
class Tag extends Model {
protected $hidden = ['pivot'];
}
Two possible ways to do this
1. using makeHidden method on resulting model
$items = Item::with('tags')->get();
return $items->makeHidden(['pivot_col1', 'pivot_col2']...)
2. using array_column function of PHP
$items = Item::with('tags')->get()->toArray();
return array_column($items, 'tags');

updating and updated model events are ignored

Why the updating and updated model events are ignored when getDirty() is empty?
If there are no changes for the model, doesn't mean I'm not updating its relationships.
Update
The following code:
public function updating(Eloquent $model)
{
$history = new ContentHistory($model->getOriginal());
$history->added_on = new DateTime;
$model->history()->save($history);
}
Is never triggered if getDirty() is empty. I have added $touches = ['content'] to the ContentHistory model, but for no avail.
Check Touching Parent Timestamps:
When you update any related model and if you want to update the parent's timestamp when the child model is updated then just use the protected $touches = array('modelname'); property. For example, if you have a Post model and it has a child model as Comment and if you want to update the Post model (only updated_at timestamp field) then add a $touches property/array in the Comment model and add the name of the Parent model in that array like:
class Comment extends Eloquent {
protected $touches = array('post');
public function post()
{
return $this->belongsTo('Post');
}
}

Categories