Replicating multiple models at once - php

I am using Laravel 4.2.
If I want to duplicate a model I can use the following:
$newModel = $currentModel->replicate();
$newModel->save();
However I have this inside a loop, like so:
foreach ($this->models as $currentModel) {
$newModel = $currentModel->replicate();
$newModel->save();
}
Which obviously causes a several DB calls. I want something more efficient, so I can loop through my models and then outside of the loop use one DB call to write them all in one go.
In Laravel is there a way to replicate multiple models in one go?

You can use the insert statment of DB query builder like this :
foreach ($this->models as $currentModel) {
$newModel = $currentModel->replicate()
$newModels[] = $newModel->toArray();
}
DB::table('table_name')->insert($newModels);

No it not possible duplicate more than one model, you can see in the api documentation:
https://laravel.com/api/4.2/Illuminate/Database/Eloquent/Model.html#method_replicate
So if you would duplicate X models you need loop them and you can speific (with array parameter) wich columns you would like to not copy

Related

Laravel 5.1: get insert ids for each record inserted

my application needs a custom tagging system. This means creating multiple records for each new tag, and then adding the id's of those records to a pivot table.
I can insert multiple records from an array using Laravels "insert":
$tag_titles = explode(",", $tag_titles);
foreach ($tag_titles as $tag_title) {
$tags[] = array('tag_title' => $tag_title);
}
$tag = Tag::insert( $tags );
but now i need to know the id's that were created during the insert. Is there a way of doing this without making multiple calls to the DB to do each insert separately?
Thanks.
so far I have tried
sync() method will only work for a single id :
$tag->content()->sync($tags);
After the insert, $tag is not an object, so $tag->id won't work
It looks like you're using a QueryBuilder instance to insert, in which case, you should be able to use insertGetId
https://laravel.com/api/5.1/Illuminate/Database/Query/Builder.html#method_insertGetId

How to compare Models by ID and not content

I am using Laravel 5.1.
I have two collections, $collectionA and $collectionB. Each collection contains Flashcards which extends Model. I am trying to do something like this, but it isn't working:
$collectionA->intersect($collectionB)
The reason is that while each Flashcard has the same id, their pivot tables are different. Ideally, I would like to ignore the pivot table and compare by ids only. Is there a way to do this?
Perhaps there's a more efficient way to do this, but you could reduce $collectionB to an array of its ids, then filter $collectionA to only those elements:
$bIds = $collectionB->lists('id')->toArray();
$intersected = $collectionA->filter(function($item) use ($bIds) {
return in_array($item->id, $bIds);
}

Eloquent - Updating all models in a collection

I want to set a certain attribute in all the models of a collection.
in plain SQL:
UPDATE table SET att = 'foo' WHERE id in (1,2,3)
the code i have:
$models = MyModel::findMany([1,2,3]);
$models->update(['att'=>'foo']);
taken from here
but doesn't work. I'm getting
Call to undefined method Illuminate\Database\Eloquent\Collection::update()
the only way i have found it's building a query with the query builder but i'd rather avoid that.
You are returning a collection, not keeping the query open to update. Like your example is doing.
$models = MyModel::whereIn('id',[1,2,3]);
$models->update(['att'=>'foo']);
whereIn will query a column in your case id, the second parameter is an array of the ids you want to return, but will not execute the query. The findMany you were using was executing it thus returning a Collection of models.
If you need to get the model to use for something else you can do $collection = $models->get(); and it will return a collection of the models.
If you do not just simply write it on one line like so;
MyModel::whereIn('id',[1,2,3])->update(['att'=>'foo']);
Another option which i do not recommend is using the following;
$models = MyModel::findMany([1,2,3]);
$models->each(function ($item){
$item->update(['att'=>'foo']);
});
This will loop over all the items in the collection and update them individually. But I recommend the whereIn method.
The best solution in one single query is still:
MyModel::whereIn('id',[1,2,3])->update(['att'=>'foo']);
If you already have a collection of models and you want to do a direct update you can use modelKeys() method. Consider that after making this update your $models collection remains outdated and you may need to refresh it:
MyModel::whereIn('id', $models->modelKeys())->update(['att'=>'foo']);
$models = MyModel::findMany($models->modelKeys());
The next example I will not recommend because for every item of your $models collection a new extra query is performed:
$models->each(function ($item) {
$item->update(['att'=>'foo']);
});
or simpler, from Laravel 5.4 you can do $models->each->update(['att'=>'foo']);
However, the last example (and only the last) is good when you want to trigger some model events like saving, saved, updating, updated. Other presented solutions are touching direct the database but models are not waked up.
Just use the following:
MyModel::query()->update([
"att" => "foo"
]);
Be mindful that batch updating models won't fire callback updating and updated events. If you need those to be fired, you have to execute each update separately, for example like so (assuming $models is a collection of models):
$models->each(fn($model) => $model->update(['att'=>'foo']) );

How can you update and create a new row in Laravel from a JSON object with relationships?

I started using Laravel yesterday, the ORM seems powerful. Does it have any way of updating rows in related models? This is what I tried:
Step 1: Generate a JSON object with the exact structure the database has. The JSON object has certain fields that are subarrays which represent relationships in the database.
Step 2: Send the JSON object via POST to Laravel for processing, here it gets tricky:
I can change the JSON object into an array first
$array = (array) $JSONobject;
Now I need to update, I would expect this to work:
Product::update($JSONobject->id,$array);
But because the array has subarrays, the update SQL that is executed cannot find the sub-array column in the table, it should instead look for the associated table. Can this be done? Or do I have to call the other models as well?
Thanks in advance!
This is something that Eloquent does not handle for you. The array that you supply to the update() method should contain columns only for, in your case, the Product model. You might try something like this to update relations. This is all off the top of my head and is by no means tested. Take it with a grain of salt.
$update = (array) $JSONobject;
$relations = [];
foreach ($update as $column => $value)
{
// If the value is an array then this is actually a relation. Add it to the
// relations array and remove it from the update array.
if (is_array($value))
{
$relations[$column] = $value;
unset($update[$column]);
}
}
// Get the product from the database so we can then update it and update any of the
// the products relations.
$product = Product::find($update['id']);
$product->update($update);
foreach ($relations as $relation => $update)
{
$product->{$relation}()->update($update);
}
The above code assumes that the key for your nested relation arrays is the name of the relation (method name used in your model). You could probably wrap this up in a method on your Product model. Then just call something like Product::updateRecursively($JSONobject); I'm terrible with names but you get the idea.
This probably won't work with more complex relations either. You'd have to take it a few steps further for things like many to many (or probably even one to many).

Yii fetching records by type

I'm wondering if Yii has an efficient method for grouping items by type.
Let's say I have the following model:
Tag
------
id
name
type_id
And let's say there are 5 different types of Tags. I want to be able to display in my index all tags in sections by type_id. Is there a Yii-way of accomplishing this?
Outside a framework I would write a function such that results fetched from the DB were stored like this:
$tags[$typeID][] = $tag;
Then in each section I could do something like:
foreach( $tags[$typeID] as $tag )
{
// Here are all tags for one $typeID
}
But I'm having difficulty figuring out how to do this in Yii without:
A) looping through the entire result set first and rewriting it or,
B) running 5 different queries.
When using ActiveRecord simply specify the "index" in the DBCriteria. So in a query do:
ActiveRecordClass::model()->findAll(array('index'=>'type_id'));
That will return an assoc array that your after. TBF it probably executes exactly the same code, but this is obviously easier to use that performing it everywhere.
Assuming that your active record class is called MyActiveRecordClass, the simplest approach should be sufficient:
$models = MyActiveRecordClass::model()->findAll();
$groupedModels = array();
foreach ($models as $model) {
$groupedModels[$model->typeID][] = $model;
}
If you give more specific information about how you intend to display the grouped results it might be that a better approach can be worked out.

Categories