Update all records through model relation ship in laravel - laravel

class ModelA {
public function modelB()
{
return $this->hasMany(ModelB::class);
}
}
class ModelB {
public function modelC()
{
return $this->hasMany(ModelC::class);
}
}
class ModelC {
public function modelD()
{
return $this->hasMany(ModelD::class);
}
}
class ModelD {
//has column status_changed_date
}
I have $modelA = ModelA::find($id);
ModelA have multiple ModelB, ModelB have multiple ModelC, ModelC has multiple ModelD.
Now I want update status_changed_date for all matching records.
Is there any better way to do this. I have refered https://laravel.com/docs/5.7/eloquent-relationships#updating-many-to-many-relationships
but couldn't find out a solution.
Please help to solve this.

$modelAs = ModelA::with('modelB.modelC.modelD')->find($id)
before you can use that way :
$modelA->modelB->each->modelC->each->modelC->each->update([
'field' => 'value'
]);
or reduce values to ids, this only made one query :
$ids = [];
$modelA->modelB->each->modelC->each->modelC->each(function ($c) use($ids) {
$ids[] = $c->id;
});
ModelC::whereIn('id', $ids)->update([
'field' => 'value'
});
if each->modelC->each is not working, use method notation

Instead of making all this update, make one common table of common fields and directly update that table.
For more
Laravel Polyformic relationships

Related

Laravel, sort result on field from relation table?

I have a list with gamers and another table with game stats.
My list code is:
$gamers = Gamer::with(['lastGameStat' => function($query) {
$query->orderBy('total_points', 'DESC');
}])->paginate(20);
relation:
public function lastGameStat() {
return $this->hasOne(GameStat::class, 'gamer_id', 'id')->orderBy('created_at', 'DESC');
}
in relation table I have field: total_points and with this code I thought it's possible to sort list of gamers by total_points $query->orderBy('total_points', 'DESC');
It doesn't work, can somebody give me an advice here how can I sort the result on a field from relation table?
I guess you'll need either another relation or custom scopes to fetch various game stats of a gamer.
Second relation
Gamer.php (your model)
class Gamer
{
public function bestGameStat()
{
return $this
->hasOne(GameStat::class)
->orderBy('total_points', 'DESC');
}
}
Custom scopes
Gamer.php
class Gamer
{
public function gameStat()
{
return $this->hasOne(GameStat::class);
}
}
GameStat.php
use Illuminate\Database\Eloquent\Builder;
class GameStat
{
public function scopeBest(Builder $query)
{
return $query->orderBy('total_points', 'DESC');
}
}
In your controller:
$gamersWithTheirLatestGameStatistic = Gamer::with(['gameStat' => function($query) {
$query->latest();
}])->paginate(20);
$gamersWithTheirBestGameStatistic = Gamer::with(['gameStat' => function($query) {
$query->best();
}])->paginate(20);
Be aware as this is untested code and might not work.

Paginate with Eloquent but without instantiation Models

We have two Models:
SimpleModel (id, country, code)
ComplexRelatedModel (id, name, address)
SimpleModel has many ComplexRelatedModel, then
class Product extends Model
{
protected $fillable = [
'name'
];
/* hasOne */
public function complexRelatedChild()
{
return $this->hasOne(self::class, 'parent_id', 'id');
}
}
If we do
$simples = SimpleModel
->with('complexRelatedChild')
->simplePaginate(100000 /* a lot! */);
And we need only do
foreach ($simples as $simple) {
echo $simple->complexRelatedChild->name;
}
Any ComplexChild has hydratated and ready. This takes a lot of memory in my case. And we need just one field without any funciton or feature of Model.
It's possible use some data field from related object or with eloquent this isn't possible?
Not sure I completely understand your question. You want to only load one field from the complexRelatedChild relation to keep memory limit down?
You could do:
$simples = SimpleModel::with(['complexRelatedChild' => function($query){
return $query->select(['id', 'name']);
})
->simplePaginate(100000);
Which can be simplified to:
$simples = SimpleModel::with('complexRelatedChild:id,name')
->simplePaginate(100000);
However if I were you, I would try to paginate less items than 100000.
Update:
You could use chunk or cursor functions to process small batches of SimpleModel and keep memory limit down.
SimpleModel::chunk(200, function ($simples) {
foreach ($simples as $simple) {
}
});
or
foreach (SimpleModel::cursor() as $simple) {
}
See the documentation for more information

Laravel 5.4 How to update pivot table?

I have next structure pivot table: products_equipment_value(id, product_id, equipment_id, value_id).How to update table fields equipment_id and value_id ?
public function equipments()
{
return $this->belongsToMany('App\Equipment', ' product_equipment_value');
}
public function values()
{
return $this->belongsToMany('App\Value', ' product_equipment_value', 'product_id', 'value_id')
}
Use(not work)
$product->equipments()->detach();
foreach($data['eq'] as $key => $val){
$product->equipments()->attach([
'equipment_id' => $key,
'value_id' =>$val
]);
}
You should use withPivot function on the relations.
public function equipments()
{
return $this->belongsToMany('App\Equipment', 'product_equipment_value')->withPivot('value_id');
}
Then when you attach the models
$product->equipments()
->attach([
$key => ['value_id' =>$val],
]);
Referring to Many to Many Eloquent's relationship (https://laravel.com/docs/5.5/eloquent-relationships#many-to-many) you can sync IDs like this :
$product->equipments()->sync([array_of_equipments_ids]);
Example:
$product->equipments()->sync([3, 8, 9, 24]);
If you defined the Many to Many inverse relationship you can also do this that way from the equipment instance :
$equipment->values()->sync([array_of_values_ids]);
If you have extra columns with your pivot table you can add them like this :
public function equipments(){
return $this->belongsToMany('App\Equipment', 'product_equipment_value')->withPivot('extra_column1');
}
And so :
$product->equipments()->sync([1 => ['extra_column1' => 'Value for this column']])
Note that you can use sync() or attach() methods to construct many-to-many associations. You can take a look here : https://stackoverflow.com/a/23969879/8620746

Laravel ORM should return Relation

Is it possible to return an value for an hasOne relation directly with an Model?
For example:
$List = Element::orderBy('title')->get();
The Element has a "hasOne" Relation to an column:
public function type()
{
return $this->hasOne('App\Type', 'id', 'type_id');
}
How can i now return automatically the "type" for the Model?
At the Moment i am looping through all Elements, and build my own "Array" of Objects, including the Key of "type" in this example. But ill prefer to do this only in my Model.
Ill know how to add a "normal" property, but can it be someone from an relation?
public function getTypeAttribute($value)
{
return // how ?
}
protected $appends = array('type');
Is this possible?
Edit:
A workaround could be to use DB:: to return the correct value - but ill dont thing thats a good workaround: like:
public function getTypeAttribute($value)
{
// make a query with $this->type_id and return the value of the type_name
}
protected $appends = array('type');
you need to eager load your relations when getting the Element:
$list = Element::with('type')->orderBy('title')->get();
then access the type using
foreach ($list as $item) {
echo $item->type->type_name;
}
where type_name would be the name of a column in the types table
Make a query scope and in that scope, join your attributes from other tables.
http://laravel.com/docs/5.1/eloquent#query-scopes
http://laravel.com/docs/5.1/queries#joins

How to avoid Duplicate Methods in yii2 (generated) ActiveRecord base models

I'm getting duplicate methods when generating base models with ./yii helper/models
I know WHY this is happening, but I need a solution that holds similar data constraints while still properly generating the yii2 base models.
Sample data structure.
Alert(assignedTo, createdBy) -> Both fields are foreign keys of UserId
./yii helper/models will generate these methods in UserBase
public function getAlerts()
{
return $this->hasMany(\app\models\db\Alert::className(), ['assignedTo' => 'id']);
}
public function getAlerts()
{
return $this->hasMany(\app\models\db\Alert::className(), ['createdBy' => 'id']);
}
How can I fix this problem while only modifying the Database?
how about make sure which field has value?
public function getAlerts()
{
return $this->hasMany(\app\models\db\Alert::className(), [
!empty($this->assignedTo) ? 'assignedTo' : 'createdBy' => 'id'
]);
}

Resources