Laravel object values don't exist, but then do if json_encoded - php

There's something weird going on in my Laravel query.
So I'm getting a collection of Users - if I run it through json_encode, all its keys/values are there. If I don't, some of them are null. E.g:
Log::info(json_encode($user)); // 'tasks_from_columns' exists
Log::info($user->tasks_from_columns); // null
Log::info($user->email); // exists?
Log::info(get_object_vars($user)); // 'incrementing', 'exists', 'wasRecentlyCreated', 'timestamps'
There's a relation on the User called tasks_from_columns. It's there if I json_encode it, but null if I try to access it directly. Most other keys are accessible though, like email, id etc.
If I run it through get_object_vars, none of them are shown. Just some other ones shown above.
Anyone know what the deal is?
The query itself is
$teamWithTasks = Team::where('id',$team->id)->with(['users' => function($query) use($team){
$query->with(['tasksFromColumns' => function($secondQuery) use($team){
$secondQuery->where('created_at','>',Carbon::now()->subDays(365))->with('project')->whereHas('project', function($thirdQuery) use ($team){
$thirdQuery->where('account_id', $team->account_id);
});
}]);
}])->first();

Related

How to specify two or more conditions within find() method in Laravel?

I'm having problems fetching data using Model::find() with two conditions. I want to return one record that has the given member_id AND its role is guest:
$member = CalendarMembers::find(["member_id" => $r->member_id, "role" => "guest"]);
But this isn't working. I have one user that when created, a new register is added to calendar_members table specifying this user as parent, yet, when attempting to fetch this user automatically created by just giving the member_id, it will be fetched, which SHOULD NOT.
I cannot use other ways such as:
$member = \DB::table("calendar_members")->where([ ["member_id", "=", $r->member_id], ["role", "=", "guest"] ])->first();
Because then $member->delete() or $member->destroy() will throw an error saying either delete or destroy do not exist.
The find() method uses ID to get a record. You need to use the where() and first() methods:
CalendarMembers::where(['member_id' => $r->member_id, 'role' => 'guest'])->first();
you should use where for multiple conditions and get the first row with first() method, find is basically an alias for wehre('id', $id)
To make it clear, find() method will perform database query and returns single model object as result. so find is equal to,
Model::where('id', 1)->first().
find() returns Model object and where() returns QueryBuilder. But you can directly perform on query Builder without fetching model, like,
Model::where('id', 1)->delete().
REF : https://laravel.com/docs/5.6/eloquent

How to simplify this Laravel PHP code to one Eloquent query?

I assume that this should all be in one query in order to prevent duplicate data in the database. Is this correct?
How do I simplify this code into one Eloquent query?
$user = User::where( 'id', '=', $otherID )->first();
if( $user != null )
{
if( $user->requestReceived() )
accept_friend( $otherID );
else if( !$user->requestSent() )
{
$friend = new Friend;
$friend->user_1= $myID;
$friend->user_2 = $otherID;
$friend->accepted = 0;
$friend->save();
}
}
I assume that this should all be in one query in order to prevent
duplicate data in the database. Is this correct?
It's not correct. You prevent duplication by placing unique constraints on database level.
There's literally nothing you can do in php or any other language for that matter, that will prevent duplicates, if you don't have unique keys on your table(s). That's a simple fact, and if anyone tells you anything different - that person is blatantly wrong. I can explain why, but the explanation would be a lengthy one so I'll skip it.
Your code should be quite simple - just insert the data. Since it's not exactly clear how uniqueness is handled (it appears to be user_2, accepted, but there's an edge case), without a bit more data form you - it's not possible to suggest a complete solution.
You can always disregard what I wrote and try to go with suggested solutions, but they will fail miserably and you'll end up with duplicates.
I would say if there is a relationship between User and Friend you can simply employ Laravel's model relationship, such as:
$status = User::find($id)->friends()->updateOrCreate(['user_id' => $id], $attributes_to_update));
Thats what I would do to ensure that the new data is updated or a new one is created.
PS: I have used updateOrCreate() on Laravel 5.2.* only. And also it would be nice to actually do some check on user existence before updating else some errors might be thrown for null.
UPDATE
I'm not sure what to do. Could you explain a bit more what I should do? What about $attributes_to_update ?
Okay. Depending on what fields in the friends table marks the two friends, now using your example user_1 and user_2. By the example I gave, the $attributes_to_update would be (assuming otherID is the new friend's id):
$attributes_to_update = ['user_2' => otherID, 'accepted' => 0 ];
If your relationship between User and Friend is set properly, then the user_1 would already included in the insertion.
Furthermore,on this updateOrCreate function:
updateOrCreate($attributes_to_check, $attributes_to_update);
$attributes_to_check would mean those fields you want to check if they already exists before you create/update new one so if I want to ensure, the check is made when accepted is 0 then I can pass both say `['user_1' => 1, 'accepted' => 0]
Hope this is clearer now.
I'm assuming "friends" here represents a many-to-many relation between users. Apparently friend requests from one user (myID) to another (otherId).
You can represent that with Eloquent as:
class User extends Model
{
//...
public function friends()
{
return $this->belongsToMany(User::class, 'friends', 'myId', 'otherId')->withPivot('accepted');
}
}
That is, no need for Friend model.
Then, I think this is equivalent to what you want to accomplish (if not, please update with clarification):
$me = User::find($myId);
$me->friends()->syncWithoutDetaching([$otherId => ['accepted' => 0]]);
(accepted 0 or 1, according to your business logic).
This sync method prevents duplicate inserts, and updates or creates any row for the given pair of "myId - otherId". You can set any number of additional fields in the pivot table with this method.
However, I agree with #Mjh about setting unique constraints at database level as well.
For this kind of issue, First of all, you have to enjoy the code and database if you are working in laravel. For this first you create realtionship between both table friend and user in database as well as in Models . Also you have to use unique in database .
$data= array('accepted' => 0);
User::find($otherID)->friends()->updateOrCreate(['user_id', $otherID], $data));
This is query you can work with this . Also you can pass multiple condition here. Thanks
You can use firstOrCreate/ firstOrNew methods (https://laravel.com/docs/5.3/eloquent)
Example (from docs) :
// Retrieve the flight by the attributes, or create it if it doesn't exist...
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);
// Retrieve the flight by the attributes, or instantiate a new instance...
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);
use `firstOrCreate' it will do same as you did manually.
Definition of FirstOrCreate copied from the Laravel Manual.
The firstOrCreate method will attempt to locate a database record using the given column / value pairs. If the model can not be found in the database, a record will be inserted with the given attributes.
So according to that you should try :
$user = User::where( 'id', '=', $otherID )->first();
$friend=Friend::firstOrCreate(['user_id' => $myId], ['user_2' => $otherId]);
It will check with both IDs if not exists then create record in friends table.

Phalcon find with columns parameter returns rows instead models

When I use Model::find() response is Resultset of Models, but when I add columns parameter to restrict returned columns the response is Resultset of Rows.
Example:
// Resultset of Models
$users = \Models\Users\Users::find();
// Resultset of Rows
$users = \Models\Users\Users::find([
'columns' => 'id, email'
]);
That makes me unable to call model methods. Is there a way to have Resultset of Models with columns restriction in ::find() method? I'm not sure, but this seems like bug, since Phalcon docs says:
While findFirst() returns directly an instance of the called class (when there is data to be returned), the find() method returns a Phalcon\Mvc\Model\Resultset\Simple.
And there is nothing about the exception of this rule when using columns parameter.
I would also note that other parameters of ::find() like condition, order, bind etc. works fine (Models returned).
Phalcon 1.3.4
This is not a bug, it is the expected behaviour.
Info in the docs scroll a bit down to the Parameters table and read description of columns.
If you need to use model methods or relations you should not specify columns. But if you are after better performance and do not need model relations you should use the Query Builder.
Rest of the find() parameters like condition, order e.t.c. will, not affect your ability to use model methods.
The findFirst() method is also working like the find() method. Example here:
No columns specified:
News::findFirst(3);
// Output
Models\News Object
(
...
When specifying columns
News::findFirst([
'columns' => 'id, created_at'
]);
// Output
Phalcon\Mvc\Model\Row Object
(
[id] => 1
[created_at] => 2016-02-02
)

Use collection->get() instead of collection->pluck()

I am using laravel-permission for managing roles and displaying content. Per the docs you can retrieve a users roles by using $roles = $user->roles()->pluck('name'). My problem is that the data returned is ["admin"] rather than just admin. I was reviewing the collections methods and it looked like get('name') would return what I was looking for. When I try to use the following command Auth::user()->roles()->get('name') I get
1/1
ErrorException in BelongsToMany.php line 360:
Argument 1 passed to Illuminate\Database\Eloquent\Relations\BelongsToMany::getSelectColumns() must be of the type array, string given
It seems to me like the get() method is expecting an array however, I'm trying to reference an item in the array. The raw output of Auth::user()->roles()->get() is [{"id":1,"name":"admin","created_at":"2016-03-10 06:24:47","updated_at":"2016-03-10 06:24:47","pivot":{"user_id":1,"role_id":1}}]
I have found a workaround for pulling the correct content, but it is using regex for removing the unwanted characters that are included in the pluck() method.
preg_replace('/\W/i','', Auth::user()->roles()->pluck('name'))
It seems like I'm missing something or approaching using the get() method incorrectly. Any advice is appreciated.
I think pluck() will return the value of the given column for each model in the collection, which would explain the array. In your case it looks like the user has only one role, so you get an array with only one item. If the user had multiple roles, you would likely get an array with multiple items in it.
On the other hand, the get() method is used to execute a query against the database after a query is built. The results of the query are what is returned. To return a collection of models with only a single value you will need to pass an array with just the one column you want, but that will just select models, which does not appear to be what you ultimately need.
You can try this instead: $roles = $user->roles()->first()->name
The call to first() will grab the first model in the collection returned by roles(), and then you can grab the name of the role from that model.
I typically throw some error checking around this:
$role = $user->roles()->first();
if (is_null($role)) {
//Handle what happens if no role comes back
}
$role_name = $role->name;
That's because an user can have many roles, so roles is a collection.
If you are 100% sure that a user will have only one role, you can easily do
Auth::user()->roles()->first()->name
That will get the first item of that collection (the role) and then its name.

PHP/Laravel - Assign new model to a fetched one

Let's say I make a call to my User and return the following:
$user = User::with('permissions')->find(1);
I expect to get a user with $user->permissions being the permission of the user.
Next, I create and assign some new permissions to this user by:
// Say Input::all() contains an array of new permissions I want to add
// I am using Underscore PHP here
$newPermissions = Arrays::each(Input::all(), function($permission) {
$new = new Permission($permission);
$user->associate($new);
return $new;
});
Now I want to update the $user and return them back:
// This does NOT work (it returns the original $user->permissions)
return $user->permissions = $newPermissions;
// But this DOES work
unset($user->permissions);
return $user->permissions = $newPermissions;
Is this a PHP thing or Laravel thing? And what can I do? (btw, even if I say $user->permissions ='anything, text, string, or object doesnt work!' nothing happens).
It looks like permissions is a relationship, not a simple value that you can assign values to. I'd recommend reading the documentation on relationships, eager loading, and on inserting related models.
That last one, in particular, is what you're trying to do. Instead of assigning a value to $user->permissions, you need to update the relationship with either the save, associate, or attach methods, depending on the type of relationship.

Categories