Update model which having multiple rows - laravel

I have an object model which have multiple rows like result getting with this query:
$cities = City::whereIn('id' , [1,2,3])->get();
What I want to do is update each row with the same value without using each because each is making a query on each row, so in above query I will have 3 queries.
Instead of doing this:
$cities->each->update(['name' => 'test']);
I want to do something like this as I already have the model object, but it doesn't work:
$cities->update(['name' => 'test']);
Instead I must do something like this to make it work:
City::whereIn('id' , $cities->pluck('id'))->update(['Avatar' => 'test']);
My question is; Why can't I use this:
$cities->update(['name' => 'test']);

When you use ->get() method you are getting not a model, but an collection of models.
There is no such thing as multiple row models. Models has only one "row".

There is an example for that in the documentation:
App\Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]);
See the mass update section.
So, in your case the query must be:
City::whereIn('id' , [1,2,3])
->update(['name' => 'test']);

Related

Bulk updates of related models

Update single...
$modelA->update(['status_id' => 1])
Update multiple...
ModelA::whereIn('id', $data['ids'])->update(['status_id' => 1])
SO
Updating a model related to a single row can work as follows...
$modelA->relatedModel()->update(['status_id' => 1])
($modelA instance is supplied via route model binding, and the relatedModel() method/relationship is defined in the "ModelA" model)
BUT
When attempting the following,
ModelA::whereIn('id', $data['ids'])->relatedModel()->update(['status_id' => 1])
I get an error
Call to undefined method Illuminate\Database\Eloquent\Builder::relatedModel()
It seems that I am not really understanding what is being returned by
ModelA::whereIn('id', $data['ids'])
So what I'm looking for is a method of updating the related models of multiple rows (obviously without a loop of any kind).
whereIn() and many other methods, like where(), has(), with(), etc. returns a Builder instance, which lets you chain additional queries. You cannot access ModelA instance methods, like relatedModel() on a Builder instance.
You will need to call ->get(), and iterate the results calling relatedModel()->update():
foreach(ModelA::whereIn('id', $data['ids'])->get() as $modelA) {
$modelA->relatedModel()->update(['status_id' => 1]);
}
Alternatively, if you'd like a single-liner without a loop, you will need to query the ModelB model (or whatever relatedModel() is) directly:
ModelB::whereIn('model_a_id', $data['ids'])->update(['status_id' => 1]);
Note: Assumed that ModelB has a model_a_id. If it doesn't, you can use the whereHas() method instead:
ModelB::whereHas('modelA', function ($query) use ($data) {
return $query->whereIn('model_a.id', $data['ids'];
})->update(['status_id' => 1]);
Note: Again, made some assumptions on your table/column names; adjust as required.

one-many realtionship update with changed table format

if there are two tables show and show_detail and show table has fields like 'id','show_name'(array) like ["show_1","show_2","famous_show_12"] and show_detail has fields like 'show_id','show_name','show_ids'..then how to update show_detail table with update in table show using one-many relationship.
i have tried using updateRich() method but fail.
Use Like It :-
$id = 123;
$show = 'test show';
DB::table('show')->where('id', $id)->update(['show_name' => $show]);
DB::table('show_detail')->whereIn('show_ids', [1, 2])->update(['show_name' => $show]);

Difference between [attributes:protected] and [original:protected]

Please could anyone explain to me a difference between [attributes:protected] array and [original:protected] array in laravel when using print_r to an array?
When Model reads data from table, arrays 'original' and 'attribute' contains same data. When you change the attribute value (ex $user->name='John'), the change is reflected only on the 'attributes' array but 'original' remains same. (hence the name).
When update() on a model is called, method checks what has changed comparing two arrays and construct query only for changed fields. Thus, in the case of $users->name change Laravel will not create this code:
UPDATE users set name = 'John', password = 'pass', email = 'email' where id = 1
but this:
UPDATE users set name = 'John' where id = 1
This may not be the only way Eloquent uses 'original' array. I found clockwork helpful when you need to see what's going on under the hood of Eloquent.

Laravel Update only 1 row?

Is there a way to update only 1 row in Laravel 5? I want to set the value of selected to true from only 1 row, ordered by id:
Ill tried:
DB::table('user_tabs')->where('user_id', '=', $user_id)->orderBy('id', 'desc')->take(1)->update(array('selected' => true));
And:
DB::table('user_tabs')->where('user_id', '=', $user_id)->orderBy('id', 'desc')->first()->update(array('selected' => true));
but it is not working. Any ideas?
Try this:
DB::table('user_tabs')->where('user_id', $user_id)->update(['selected' => true]);
You were trying to update the record after selecting it, which confused the Query Builder.
Also, have a look at Eloquent models. They make database interactions a little nicer.

Cake php 2: Sort on 2nd-level Association

I wrote a post last year about this. The first part, how to sort on a 1st-level Association, was answered (thanks again!) but the second part of the question, about how to sort on a 2nd-level Association, was never answered. It wasn't a big issue, and we ran out of time so I never actually implemented it. But now we're updating the web, and the client wants this functionality, namely, with the following Models:
Company belongsTo City
City belongsTo Country, hasMany Companies
Country hasMany Cities
In the Companies page I want to sort on City.Country.name. Even putting recursive=2, it doesn't work. Cake ignores my 'order' condition; in the generated SQL there simply is no 'order by' at all.
It works fine if I sort on sort on City.name, however.
Is there any way to do this? I've been scouring the docs and Stackoverflow. I've looked at virtual fields, custom queries.
One way that seemed to look promising was to use Model->query() in the CompaniesController:
$companies = $this->Company->query("SELECT * FROM companies com left join cities c on c.id = com.city_id left join countries c2 on c2.id = c.country_id order by c2.name");
But, is this the best/only way to go? And do I not have to now worry about overriding pagination? I don't mind that but I would still like to use "normal" built-in pagination elsewhere for Companies. Will that be possible?
Also, in the examples in the docs, it says to do something like Model->query('SELECT * FROM pictures AS Picture LIMIT 2') so the resulting Array will use the model name as the array key. But how can I do this with my complex query? Where would the "AS" go?
I was rather hoping I'd be able to avoid having to do it like this though. Is there a simpler way to do it?
EDIT
Hi, thanks for your help. By "pagination technique" you mean Cake's built-in pagination? Yes, that's what I want. My default paging conditions in the controller are:
$this->paginate = array('conditions' => 'order' => array('Company.name' => 'ASC');
And it sorts on company name. The SQL is
SELECT Company.id, etc. FROM companies AS Company LEFT JOIN cities AS City ON Company.city_id = City.id order by Company.name
And when I create paging links in the View like this
$paginator->sort('City.name')
it adds these parameters to the url
.../companies/sort:City.name/direction:desc
it sorts on City name. The SQL is
SELECT Company.id, etc. FROM companies AS Company LEFT JOIN cities AS City ON Company.city_id = City.id order by City.name
But when I try this:
$paginator->sort('City.Country.name');
it adds these parameters to the url
.../companies/sort:City.Country.name/direction:asc
and the generated SQL is
SELECT Company.id, etc. FROM companies LEFT JOIN cities AS City ON (Company.city_id = City.id)
It completely ignores the 'sort' condition, and there is no 'order by' at all. I'm not sure why. Maybe Cake just can't do this? I have 'recursive' set to 2.
The other option is Model->query, which I tried and got working, but I'd rather not use because I would have to override paginate and paginateCount methods, which isn't that hard, but the problem is that on the same page, and on other pages, I am already using 'normal' Cake paging for Companies. So if I override paginate and paginateCount, won't I have to change all of those pages to use the new, over-ridden paging? I'd like to avoid that, because it seems like overkill, and is working fine everywhere else, except for this one case.
Any assistance much appreciated.
Bob
Well, in the end I managed it, after perusing the docs and many examples online, like this:
First, in the Model, declare and implement custom findMethods.
class Company extends AppModel {
public $findMethods = array('companiesOrderByCountry' => true);
protected function _findCompaniesOrderByCountry($state, $query, $results = array()) {
if ($state === 'before') {
$query['joins'] = array(
array(
'table' => 'cities',
'alias' => 'City',
'type' => 'LEFT',
'conditions' => 'City.id = Company.city_id'
),
array(
'table' => 'countries',
'alias' => 'Country',
'type' => 'LEFT',
'conditions' => 'Country.id = City.country_id'
)
);
$query['order'] = array('Country.name' => 'asc');
return $query;
}
return $results;
}
Then in the Controller, conditionally call it, based on named params in request
if (!empty($this->request->params['named']['sort']) && $this->request->params['named']['sort'] == 'Country.name') {
// here we need to sort on Country.name. Call custom findMethod in Model
$this->paginate = array('companiesOrderByCountry','limit' => 10);
$companies = $this->paginate();
$this->set(compact('companies'));
} else {
// do a normal search
$this->paginate = array('limit' => 10,'conditions' => array('order' => array('Company.nombre' => 'ASC' ));
$companies = $this->paginate('Company');
$this->set('companies', $companies);
}
Lastly, create a link, passing the named parameter, in the .ctp
<?php $paginator = $this->Paginator; ?>
<?php echo $paginator->sort('Country.name', __('Country')); ?>
There's probably a more elegant way to do this, but I was sick of dealing with it. The generated sql is just what I need and now I can sort on Country, which is a 2nd-level (Company->City->Country) association.
Hope this helps someone someday!
Bob
If you want to try using the query method, then I would not use the asterisks (*). Instead explicitly list the fields you want.
Your query should look something like this: {My personal best practice, use COALESCE on fields that may be NULL - especially when using a LEFT JOIN}
SELECT companies.name AS CompanyName,
COALESCE(cities.name, 'Unknown') AS CityName,
COALESCE(countries.name, 'Unknown') AS CountryName
FROM companies
LEFT JOIN cities
ON companies.city_id = cities.id
LEFT JOIN countries
ON cities.country_id = countries.id
ORDER BY CountryName, CityName
Use AS to separate the field from its alias. It also separates a table from its alias ( e.g., the examples you listed.) AS is optional, but I feel it adds to readability.
--
(If you want to try the pagination technique, please show the MySQL SELECT statement that is failing when RECURSIVE = 2, along with what the controller's code looks like - specifically the 'order'=> array)

Resources