Combine collection and relation Eloquent - laravel

Is there a way to integrate the relation columns into the parent collection?
$r = User::with('contactDetails')->get();
Then be able to do merge them into a single object without having to do $r->user[0]->contactDetails->phonenumber but instead just $r->user[0]->phonenumber

You may use transform to iterate the collection and map on the existing object. It will be something like this;
User::with('contactDetails')
->get()
->transform(function (User $user) {
$user->phonenumber = $user->contactDetails->phonenumber;
return $user;
});
Another and more performant way could be using join since it is 1-to-1 relationship. Then you may put phonenumber into your select statement.

Related

Get data from another model

$objects = Objects::where("id_host", $hostId)
->orderBy("id", "desc")
->get();
In the collection , each object has a type_id field . How can I use this field to get a record in another model and mix them into this object in the response
First thing is first, in order to show the relationship between the records, you'll need to set up a One-to-Many/Many-to-One relationship. This allows you to readily call those relationships from within Laravel and to load them together.
Without being able to see your Type and Object classes, I really can't give specific advice on this, but it should look something like this:
Objects
public function type(): BelongsTo
{
return $this->belongsTo(Type::class);
}
Type
public function objects(): HasMany
{
return $this->hasMany(Objects::class);
}
Once you've done that, you can add a with(...) call to your Eloquent query to eager load the relationship.
$objects = Objects::where("id_host", $hostId)
->orderBy("id", "desc")
->with('type')
->get();
Alternatively, if you don't want to eager load it for some reason, you can call $object->type to get the Type object.

Detach on eloquent collection not working

In a Post model I have:
public function tags()
{
return $this->morphToMany(\App\Models\Tag::class, 'taggable');
}
But when I then do:
$posts = Post::all();
$posts->tags()->detach();
I get
Method Illuminate\Database\Eloquent\Collection::inputs does not exist.
Based on your code, it seems like you're trying to remove all the tags from all the existing posts in your application.
If this is what you're trying to do, then follow #A.A Noman comment: you should detach them by iterating the collection, one by one.
Another option is to just clear the intermediate table containing the relations.
If what you're trying to do here is detaching all the tags from a single Post, you can search the post and then detach all the tags:
$post = Post::find($id);
$post->tags()->detach();
UPDATE
To iterate the collection and remove all the Tags from all the Posts:
$posts = Post::all();
foreach ($posts as $post) {
$post->tags()->detach();
}
Use Laravel's with()
https://laravel.com/docs/8.x/eloquent-relationships
To bring the tags related to this collection
Example:
$posts = Post::with('tags');
$posts->tags()->detach();
Likewise, if you want to remove a particular entity relationship from the pivot table, you can use the detach method.
For example, if you want to remove a certain author from a book, you can do so.
$book->authors()->detach($authorId);
Or you can pass multiple IDs as an array.
$book->authors()->detach([4, 5, 8]);
Or, if you want to remove all authors from a book, use detach without passing any arguments.
$book->authors()->detach();

How to pluck unique relationships from query builder?

I need to pluck unique set of relation which are present in query set of models. I have model User with table 'users' and model Role with table 'roles', User hasMany Role.
//User.php
public function roles()
{
return $this->belongsToMany(Role::class);
}
//Role.php
public function users()
{
return $this->belongsToMany(User::class);
}
I managed it using collections, but it needs to run such a big query, what slowing whole request
//Controller
...
$users = User::query()->someChainOfScopes();
$uniqueRoles = $users->get()->pluck('roles')->flatten(1)->unique('id')->values();
...
This code returns collection which I need, but I would like to implement it using query builder to pluck unique roles for speed improvement
There are a few different ways you could do this using Laravel,
Joins could be an option to join the 2 tables in a DB::table() query and then find the users using the parameters you require and return the role_id.
To speed up users
$user_ids = DB::table('users')->select('id')->someChainOfScopes()->get();
The chained scopes you will have to change to standard where functions etc.
Then use this array to query the role_user table direct
$role_ids = DB::table('role_user')
->select('role_id')
->whereIn('user_id', $user_ids)
->distinct()
->get();
The problem with collections and plucking from collections is it has to iterate over the whole array of objects just to pull a field out. When that is a large collection then iis very costly.
I've not tested but hopefully it will get you going in the right direction.

orderBy() a relationship without using joins

Up until now, I've been doing this to order by a model's relationship:
$users = User::leftJoin('user_stats', 'users.id', '=', 'user_stats.user_id')
->orderBy('user_stats.num_followers')
->get();
Here's the User model:
class User extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'users';
public function stats()
{
return $this->hasOne('App\Models\UserStat');
}
}
Is there a better way to orderBy a model's relationship without having to use any joins? I'm using Laravel 5.2.
You could use Collection instead:
$users = User::with('stats')->get()->sortBy(function($user, $key) {
return $user['stats']['num_followers'];
})->values()->all();
https://laravel.com/docs/5.2/collections#method-sortby
As long as the column you want to order by is on a different table (eg. on a related model), you'll have to join that table in.
The Collection sort:
The proposal by crabbly does not save you the join. It only sorts the collection manually in php, after the data has been fetched using a join.
Edit: In fact, the with('stats') generates no join, but instead a separate SELECT * FROM user_stats WHERE ID IN (x, y, z, …). So well, it actually saves you a join.
Maintain a copy:
A way of truly saving the join would be to have a copy of num_followers directly in the users table. Or maybe store it only there, so you don't have to keep the values in sync.

Laravel 4 - Get Array of Attributes from Collection

I have a collection of objects. Let's say the objects are tags:
$tags = Tag::all();
I want to retrieve a certain attribute for each tag, say its name. Of course I can do
foreach ($tags as $tag) {
$tag_names[] = $tag->name;
}
But is there a more laravelish solution to this problem?
Something like $tags->name?
Collections have a lists method similar to the method for tables described by #Gadoma. It returns an array containing the value of a given attribute for each item in the collection.
To retrieve the desired array of names from my collection of tags I can simply use:
$tags->lists('name');
Update: As of laravel 5.2 lists is replaced by pluck.
$tags->pluck('name');
More specifically, the laravel 5.2 upgrade guide states that "[t]he lists method on the Collection, query builder and Eloquent query builder objects has been renamed to pluck. The method signature remains the same."
Yep, you can do it nice and easily. As the Laravel 4 Documentation states, you can do
Retrieving All Rows From A Table
$users = DB::table('users')->get();
foreach ($users as $user)
{
var_dump($user->name);
}
Retrieving A Single Row From A Table
$user = DB::table('users')->where('name', 'John')->first();
var_dump($user->name);
Retrieving A Single Column From A Row
$name = DB::table('users')->where('name', 'John')->pluck('name');
Retrieving A List Of Column Values
$roles = DB::table('roles')->lists('title');
This method will return an array of role titles.
You may also specify a custom key column for the returned array:
$roles = DB::table('roles')->lists('title', 'name');
Specifying A Select Clause
$users = DB::table('users')->select('name', 'email')->get();
$users = DB::table('users')->distinct()->get();
$users = DB::table('users')->select('name as user_name')->get();
EDIT:
The above examples show how to access data with the help of Laravel's fluent query builder. If you are using models you can access the data with Laravel's Eloquent ORM
Because Eloquent is internaly using the query builder, you can without any problem do the following things:
$tag_names = $tags->lists('tag_name_label', 'tag_name_column')->get();
which could be also done with:
$tag_names = DB::table('tags')->lists('tag_name_label', 'tag_name_column')->get();
Here are a few snippets from my own experimentation on the matter this morning. I only wish (and maybe someone else knows the solution) that the Collection had a $collection->distinct() method, so I could easily generate a list of column values based on an already filtered collection.
Thoughts?
I hope these snippets help clarify some alternative options for generating a list of unique values from a Table, Collection, and Eloquent Model.
Using a Collection (Happy)
/**
* Method A
* Store Collection to reduce queries when building multiple lists
*/
$people = Person::get();
$cities = array_unique( $people->lists('city') );
$states = array_unique( $people->lists('state') );
// etc...
Using an Eloquent Model (Happier)
/**
* Method B
* Utilize the Eloquent model's methods
* One query per list
*/
// This will return an array of unique cities present in the list
$cities = Person::distinct()->lists('city');
$states = Person::distinct()->lists('state');
Using an Eloquent Model PLUS Caching (Happiest)
/**
* Method C
* Utilize the Eloquent model's methods PLUS the built in Caching
* Queries only run once expiry is reached
*/
$expiry = 60; // One Hour
$cities = Person::remember($expiry)->distinct()->lists('city');
$states = Person::remember($expiry)->distinct()->lists('state');
I would love to hear some alternatives to this if you guys have one!
#ErikOnTheWeb
You could use array_column for this (it's a PHP 5.5 function but Laravel has a helper function that replicates the behavior, for the most part).
Something like this should suffice.
$tag_names = array_column($tags->toArray(), 'name');

Resources