Difference between laravel value() function and direct model access - php

I'm doing an Eloquent query in my Profession model, i ran an where query, retrieve the first value, then caught only the value in "id" column with ->value('id')
//This always return 1
$profession = Profession::where('name', $profession)->first()->value('id');
//This return the right value
$profession = Profession::where('name', $profession)->first()->id;
Does value() function depends on some other aspect of the query/configuration? Couldn't find anything in Laravel query builder documentation.

Profession::where('name', $profession)->first() returns a model. You cannot call value on a model. However, you may call it on a query builder as explained in the docs?
So instead of calling
$profession_id = Profession::where('name', $profession)->first()->id;
you could achive the same thing with
$profession_id = Profession::where('name', $profession)->value('id');

There is a helper function in laravel with the name value . It will always returns that value it is given for example value(true) is true
But for eloquent if you want to access the fields either you use pluck function like ->pluck(id) it will give you ids only

Related

Why is it not possible to use groupBy() in an eager loading query - Laravel

ErrorException:
stripos() expects parameter 1 to be string, object given
For the groupBy() call in the with() method
$user = User::with([
'pricelists' => function($query) {
$query->groupBy(function($var) {
return Carbon::parse($var->pivot->created_at)->format('m');
});
}
])->where('id', $id)->get();
I already saw a few posts talking about how to manage this problem and that it shall not be possible to use groupBy() in eloquent but I do not really understand why...
To be clear:
User and Pricelist model got a many-to-many relationship with the default timestamps() method. I am trying to get the downloaded pricelists grouped by their months they were downloaded from the current user.
After a few attempts I just deleted the above shown => function($query... statement from the with() method and just left the with(['pricelist']) to fetch all datasets and tried this:
$user->pricelists = $user->pricelists->groupBy(function($var) {
return Carbon::parse($var->pivot->created_at)->format('m');
});
return $user->pricelists;
And it works fine and returns an array with multiple arrays for each month... But returning it like this:
return $user;
returns just 1 array with all entries... I do not really get the sense behind it right now...
The two groupBy() method that you are using in the two code you provide are totally different methods.
The first groupBy() where you use it in the callback is actually being called by $query which is a query builder object. The groupBy() here is used to add SQL GROUP BY Statement into the query. And as per the documentation, it only take string variables as parameter.
The groupBy() in your second code is being called by $user->pricelists which is a laravel eloquent collection. The groupBy() method here is actually from the base collection class and is used to group the items inside the collection into multiple collections under the different key defined by the parameter passed to the function. Please read the documentation here.
For your case, the second groupBy() is the one you should be using since you plan to use a callback and will allow you to use more complicated logic.

Catch and save elements in Laravel, not by primaryKey

I need to get an element from the database, but I can not get it by the FIND method, since FIND only finds it by the primaryKey and what I need is not by my primaryKey. So I did like this:
$user = Pac::find($request->pac_id);
$element = query()->where('med_cart', $user->pac_id)->get();
$element->med_obs = $request->med_obs;
$element->save(); // error
Now I need to save this element, however, I can not use the SAVE method, as I believe it is fully connected with FIND and FINDORFAIL (if anyone knows, explain to me which methods I can use the SAVE method).
How can I save them the way I did? Or is there some other way to do it?
Because I need to get the element with a data other than the primaryKey and then save it, then I can not use FIND or FINDORFAIL, I think.
The function ->find() returns an Eloquent Model instance and you can then call ->save() on the model instance.
You're using ->get() which returns a Collection.
To update your query (that may target one or more entries) just perform the update statement directly from the QueryBuilder by replacing ->get() with ->update(['med_obs' => $request->med_obs]).
Be aware that when doing this you are now using Fluent queries, instead of eloquent. This means that any logic you may have defined in the boot function of your model is not evaluated.
If you are certain that you only have a single result you can append ->first() to your query, which will return a Model of the first result that matches your ->where clause. You can then call ->save() on it:
$user = Pac::find($request->pac_id);
$element = query()->where('med_cart', $user->pac_id)->first();
$element->med_obs = $request->med_obs;
$element->save();

Laravel -> Make query based on model's method result

I have 2 tables and models.
So, 1 CV has Many Diplomas. Relationship is defined in models.
In my edit action :
$cv = Cv::where('link', '=', $link)->firstOrFail();
Then If I'm calling relationship function diplomas like here :
$cvDiploms = $cv->diplomas;
return $cvDiploms;
It returns me array of JSON objects.
Now, I'm trying to make a query like :
$deletedDiplomas = $cv->diplomas->whereNotIn('image', $request['test-diploma'])->get();
It returns me that error :
Missing argument 1 for Illuminate\Support\Collection::get(), called in
/home/name/project/app/Http/Controllers/CvsController.php on line 272
and defined
How should I call that query to get all Cv's diplomas, where 'image' is not in $request['test-diploma'] ?
$cv->diplomas automatically calls get() on the query, so it returns a collection. You are then calling get() again, but the Collection method get() requires an argument.
You need to write $cv->diplomas() (referencing the function not the attribute) to fetch the query, which you can then filter before calling get()
$deletedDiplomas = $cv->diplomas()->whereNotIn('image', $request['test-diploma'])->get();
You are probably trying to make a query on an array object. You should use the relation itself to make the query. Try this:
$deletedDiplomas = $cv->diplomas()
->whereNotIn('image', $request['test-diploma'])
->get();

When not or should use get() in Laravel 5

I need to understand when/not to use get(); in Laravel 5.
PHP warning: Missing argument 1 for Illuminate\Support\Collection::get()
Google shows me answers to their issue but no one really explains when you should/not use it.
Example:
App\User::first()->timesheets->where('is_completed', true)->get(); // error
App\Timesheet::where('is_completed', true)->get(); // no error
Fix:
App\User::first()->timesheets()->where('is_completed', true)->get(); // no error
Noticed the timesheets() and not timesheets? Could I have a detail explanation for what is going on, please?
I'm coming from a Ruby background and my code is failing as I do not know when to use () or not.
I'll try to describe this as best I can, this () notation after a property returns an instance of a builder, let's take an example on relationships,
Say you have a User model that has a one-to-many relationship with Posts,
If you did it like this:
$user = App\User::first();
$user->posts();
This here will return a relationship instance because you appended the (), now when should you append the ()? you should do it whenever you want to chain other methods on it, for example:
$user->posts()->where('some query here')->first();
Now I will have a the one item I wanted.
And if I needed say all posts I can do this:
$user->posts;
or this
$user->posts()->latest()->get();
$user->posts()->all()->get();
So the key thing here is, whenever you want to chain methods onto an eloquent query use the (), if you just want to retrieve records or access properties directly on those records then do it like this:
$user->posts->title;
Well, ->timesheet returns a collection, where ->timesheet() returns a builder.
On a Collection you can use ->where(), and ->get('fieldname'), but no ->get().
The ->get() method can be used on a builder though, but this will return a collection based on the builder.
Hope this helps.
The 'problem' you are facing is due to the feature of being able to query relations
When accessing a relation like a property, ->timesheets, the query defined in the relationship is executed and the result (in the form of a Collection) is returned to you.
When accessing it like a method, ->timesheets(), the query builder is returned instead of the resulting collection, allowing you to modify the query if you desire. Since it is then a Builder object, you need to call get() to get the actual result, which is not needed in the first case.
When you use ->timesheets you are accessing a variable, which returns the value of it (in this case an instance of Collection).
When you use ->timesheets() you are invoking whatever is assigned to the variable, which in this case returns an instance of Builder.
whilst pascalvgemert's answer does answer your problem regarding Laravel, it does not explain the difference between accessing or invoking a variable.
In simple term
$user = App\User::get();
is used to fetch multiple data from database
rather
$user = App\User::first();
is used to fetch single record from database

Laravel Collections counting result

On a User model (table with 4 records), when I do:
$coll = User::all();
echo $coll->count();
I get the amount of records found (4).
But when I do:
$coll = User::find(2);
echo $coll->count();
I do not get 1 (As I expect) but the amount of attributes in the resulting collection (23 in this case).
How can I check if more then one records are found?
UPDATE:
OK, thanks to you all I now see the difference in result between collection and model.
But my real problem is that I have to detect if I am having a model or a collection as a result. Depending on this result I perform some changes on the contents of the fields in the items (with map()) or model. How can I detect if the result is a model or a collection?
if(count($coll) > 1)
Works, but is this the right approach?
Here's what's going on with the code you have there:
1. When calling User::all() you'll get a Illuminate\Database\Eloquent\Collection on which you can call count which counts the elements in the collection like so:
public function count()
{
return count($this->items);
}
This will return the number of items in the collection as you correctly expected.
2. When calling User::find(2) however, the Eloquent Query Builder will not return a Collection, because it will check to see how many results there are, and since you passed one ID you'll get at most one result, so it will return an Eloquent Model instead. The Model does not have a count() method, so when you try to call $coll->count(); it will go to the magic __call method that the class has implemented which looks like this:
public function __call($method, $parameters)
{
if (in_array($method, array('increment', 'decrement')))
{
return call_user_func_array(array($this, $method), $parameters);
}
$query = $this->newQuery();
return call_user_func_array(array($query, $method), $parameters);
}
As you can see the method tries to see if it should call a couple of hardcoded methods (increment and decrement), which of course don't match in this case because $method = 'count', so it continues to create a new Query on which it will call the count method.
The bottom line is that both the first and second code samples end up doing the same thing: counting all the entries in the users table.
And since, as I pointed our above, one ID cannot match more than one row (since IDs are unique), the answer to your question is that there's no need or way to count the results of find(2), since it can only be 0 (if null is returned) or 1 (if a Model is returned).
UPDATE
First of all, for future reference you can use the PHP get_class to determine the class name of an object or get_parent_class to determine the class it is extending. In your case the second function get_parent_class might be useful for determining the model class since the User class extends a Laravel abstract Model class.
So if you have a model get_class($coll) will report User, but get_parent_class($coll) will report \Illuminate\Database\Eloquent\Model.
Now to check if the result is a Collection or a Model you can use instanceof:
instanceof is used to determine whether a PHP variable is an instantiated object of a certain class
Your checks should look something like this:
// Check if it's a Collection
if ($coll instanceof \Illuminate\Support\Collection)
// Check if it's a Model
if ($coll instanceof \Illuminate\Database\Eloquent\Model)
You might also want to check if the result is null, since find will return null if no entry is found with the given ID:
if (is_null($coll))
It seems you are expecting the find()-method to behave differently. From the docs
Find a model by its primary key.
If you problem is by checking if its from collection. Why don't you check it if its from Illuminate\Database\Eloquent\Collection.
if (get_class($coll) == 'Illuminate\Database\Eloquent\Collection') {
your code...
}
or
if ($coll instanceof \Illuminate\Database\Eloquent\Collection) {
your code...
}

Categories