updating and updated model events are ignored - php

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');
}
}

Related

Laravel Ignore with defined in Eloquent Model

I defined an Eloquent model in Laravel AssessmentCategory with a with relation with another Eloquent model AssessmentQuestion:
<?php
namespace App\Http\Models;
use Illuminate\Database\Eloquent\Model;
class AssessmentCategory extends Model {
protected $table = 'assessment_categories';
protected $with = ['questions'];
public function scopeJsonColumns($query) {
return $query->selectRaw('assessment_categories.id, assessment_categories.title');
}
public function questions() {
return $this->hasMany(AssessmentQuestion::class, 'category_id');
}
}
the AssessmentCategory now always loads its relation (child) data, but I want to call the AssessmentCategory somewhere without loading the relation (child) data.
AssessmentCategory::jsonColumns()->get();
You can use without() Method when want to get data without relation.
AssessmentCategory::without('questions')->jsonColumns()->get();
There is multiple way to fix you problem.
You can disable eager loading for current query at all:
AssessmentCategory::setEagerLoads([])->get();
You can exclude only one relation from eager loading:
AssessmentCategory::without('questions')->get();
Or you can disable eager loading at all by removing:
protected $with = ['questions'];
note that in this case you need yo overwrite all previous queries.

How to let Laravel pivot table use only created_at?

When dealing with belongsToMany relation, you use a pivot table to record the relation.
For many pivot tables, the relations are just created and then deleted. They won't have their own property, so you never update them.
I know I can do this to auto-set both updated_at and created_at.
class Foo extends Model
{
public function bars() {
$this->belongsToMany(Bar::class)->withTimestamps();
}
}
But how to use only created_at? I have no idea.
You could take another approach:
Skip the withTimestamps() method (to avoid adding both created_at and updated_at columns).
Add a custom pivot column: created_at.
So your code:
class Foo extends Model
{
public function bars() {
$this->belongsToMany(Bar::class)->withPivot('created_at');
} // ^^^^^^^^^^^^^^^^^^^^^^^^
}
Now, with this approach you will need to set the value of created_at manually when creating records:
$foo = Foo::find(1);
$foo->bars()->attach($someId, ['created_at' => now()->format('d-m-Y H:i:s')]);
// or whatever you use as date ^^^^^^^^^^^^^
Also, this column won't be casted as a Date by default -as opposed to what Laravel do with timestamp columns- so this:
$foo->bars()->first()->pivot->created_at
won't be an instance of Carbon. If you want it though, you could create a custom Pivot model, then specify the column to cast and update your relationship to use the custom Pivot model:
Pivot model FooBar.php
class FooBar extends Pivot // <--
{
protected $casts = [
'created_at' => 'datetime:d-m-Y H:i:s',
];
}
Then in your Foo.php class:
class Foo extends Model
{
public function bars() {
$this->belongsToMany(Bar::class)->using(FooBar::class)->withPivot('created_at');
// ^^^^^^^^^^^^^^^^^^^^
}
}

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

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');
}
}

How to return one property of a belongsTo model with the child model in Laravel resource controller

I'm using Laravel as a REST API for a SPA. I have a relationship where families have multiple contributions. The contributions table has a foreign key reference to family's id. I can call on the contributions route with the hasMany/belongsTo set up, and every contribution gets the entire family model it belongs to. But I don't need all that data, I just need a single field from the family table (not the id, but a different field) with each contribution.
Here are my models and resource controller:
class Family extends Eloquent {
protected $table = 'families';
// relationships
public function contributions() {
return $this->hasMany('Contribution');
}
}
class Contribution extends Eloquent {
protected $table = 'contributions';
// relationships
public function family() {
return $this->belongsTo('Family');
}
public function other_field() {
return $this->belongsTo('Family')->select('other_field');
}
}
class ContributionController extends BaseController {
public function index()
{
// works - but returns the entire family with every contribution
$contributions = Contribution::with('family')->get();
// returns other_field == null with every contribution
$contributions = Contribution::with('other_field')->get();
return Response::json($contributions->toArray(),
200);
}
Where am I going wrong with selecting this single field from the belongsTo relationship?
You can use query constraints on the relationship if you use eager loading.
Family::with(['contributions', function($query)
{
$query->select('column');
}])->get();

How do I get the `pivot table` model to trigger the save/saved model events when attach or detach are called in Laravel?

In Laravel 4 how do I get the pivot table model to trigger the save/saved model events when attach or detach are called?
It seems that the pivot table 'TeamUser' below is not actually needed for the attach/detach methods to work, so at a guess I believe that the model representing the pivot table is never invoked. And so the events are never triggered.
To ask another way:
When I call User::with('Team')->find(1)->teams()->attach(1); how to get TeamUser to trigger it's own events. Note that the above attach works perfectly fine, all the records are updated in the database.
User
class User extends Eloquent
{
// Relationship
public function teams()
{
return $this->belongsToMany('Team');
}
}
Team
class Team extends Eloquent
{
// Relationship
public function users()
{
return $this->belongsToMany('User');
}
}
Team User - The model for the TeamUser table/pivot table
class TeamUser extends Eloquent
{
public function save(array $options = array())
{
// do cool stuff when relationship is saved via
// attach/detach
}
}
You can add a $touches to the Team model.
class Team extends Eloquent
{
protected $touches = array('TeamUser');
// Relationship
public function users()
{
return $this->belongsToMany('User');
}
}
This will update the TeamUser updated_at field. I think this will also hit the save() method in the TeamUser model.
If that doesn't work, you can fire your own event in the Team save() method and listen to this event somewhere.
Event::fire('team.updated', array($team));
With the new version of Laraval 5.8, Pivot tables trigger the boot events. Check the release notes: https://laravel.com/docs/5.8/releases#pivot

Categories