Why can't I use latest() in my Eloquent code? - php

I have been getting an error
'Method Illuminate\Database\Eloquent\Collection::latest does not exist' on this line of my code
$items=Item::select('*')
->orderBy('version','DESC')
->get()
->unique('name')->latest()->paginate(5);
I'm not really sure why and how should I change my code?

latest() is a Builder class method not a Collection class method see: https://laravel.com/api/8.x/Illuminate/Database/Eloquent/Builder.html#method_latest
So use it before getting the collection by get() or paginate():
$items=Item::select('*')
->orderBy('version','DESC')
->latest()
->get()
->unique('name');
Using pagination on unique values is a bit more complicated. We would need to know what to select if there is more records with the same name. The most simple approach is to use distinct() but this may not be what you need:
$items=Item::select('*')
->orderBy('version','DESC')
->latest()
->distinct()
->paginate(5);
Other possibility is to use groupBy('name') instead of distinct(), but there you must carefully aggregate the other columns around the 'name' (or select only the 'name' column). Example:
$items=Item::select('name', DB::raw('MAX(version) as max_version'))
->orderBy('max_version','DESC')
->group_by('name')
->paginate(5);
If you need more columns selected use appropriate aggregate functions MAX() or FIRST(), LAST(), AVG(), etc..
To get the whole row (simple '*' cannot be used):
$max_version_table = Item::select('model_name', DB::raw('MAX(version) as max_version'))->groupBy('model_name');
$items = Item::select('*')->joinSub($max_version_table, 'max_version_table', function($join){
$join->on('items.model_name','=','max_version_table.model_name');
$join->on('items.version','=','max_version_table.max_version');
})->paginate(5);

Laravel's latest method doesn't work on the collection, it works on Query-Builder. So, first, use the latest method and then use paginate.
$items = Item::select('*')
->orderBy('version','DESC')
->latest()
->distinct()
->paginate(5);

Related

Laravel: Orderby in related table and with

This is my model
User
Role
and relationship between of those models are many to many.
I want to create query like this:
return User::with('roles')->orderBy('roles.id')->paginate();
I don't want join because I created a base class for every model. I also don't want use orderBy after get because it must load all of my data and after that I should be able to sort and paginate it. So it is not a very good idea.
You can try something like this:
return User::with(['roles' => function ($query) {
$query->orderBy('id', 'desc');
}])->paginate();
But this will only order the eager loading attributes, but if you are interested to use join you can have something like this:
return User::with('roles')
->join('roles', 'user.id', '=', 'roles.user_id')
->orderBy('roles.id', 'desc')
->paginate();
In this you can easily use paginate which is your main concern.
Hope this helps.
User::where('role_id','!=','0')->orderBy('role_id','DESC')->paginate(10);

Method orderBy does not exist - laravel

How can i use the orderBy method with this script:
public function getProposalByUser($userId)
{
return ProposalModel::where('user_id', '=', $userId)->paginate(4)->orderBy('desc');
}
the script is to select the proposal where user have. and i want to arrange it for the top is newest.
Thanks in advance, and sorry for my bad english..
It seems the main problem is that you have used the methods in the wrong order.
public function getProposalByUser($userId)
{
return ProposalModel::where('user_id', '=', $userId)->orderBy('desc')->paginate(4);
}
paginate actually returns the query results in a collection, and the collection doesn't have an orderBy method, so you can't chain it like you can with the other query builder methods.
Secondly, it looks like the orderBy method needs another argument, unless your entity actually does have a property called 'desc'.
Try this:
return ProposalModel::where('user_id', '=', $userId)->orderBy('user_id', 'desc')->paginate(4);
Try this:
return ProposalModel::where('user_id', '=', $userId)->orderBy('created_date', 'desc')->paginate(4);

How to add a condition in Eloquent query based on related model data?

If you have a query that uses eager loading like this:
Brand::with('tags')
->where('id', $id)
->get();
A brand can have many tags.
I then also have an array of tag ids like this [2,4]. How do I add a condition to this query where it returns only those brands whose tags are in the array?
I tried the eager load constraints but that condition is then placed on the tags model, not the Brand.
I tried this also but it returns an unknown method error:
public function tagsIn($allTags){
return $this->belongsToMany('App\Tag', 'brand_tags')
->whereIn('tags.id', $allTags);
}
Brand::with('tags')
->tagsIn('[2,4]')
->get();
I suspect a possible limitation to getting it to work is the fact that Eloquent makes two separate database calls. But is there a way nevertheless?
DB::table('Brands')
->join('brand_tag','brands.id','=','brand_tag.brand_id')
->join('tags','brand_tag.tag_id','=','tags.id')
->whereIn('tags.id',$allTags)
->get();
Try this DB::table('name')->whereIn('column', array(1, 2, 3))->get();
I think you should use this package to handle tag. I used it in my projects. laravel-tagging

Eloquent eager loading acting as explicit join

I am building a small blog and I would like to use the built-in Eloquent eager loading, but I would like it to act as an explicit join.
Here's what I'm trying to do, using a join, which works, but not in the way I want.
$posts = Post::join('users', function($join){
$join->on('users.id', '=', 'posts.user_id');
$join->on('users.status', '=', DB::raw('active'));
})
->get();
My problem with this is that I can't use the user model on my post like so:
$posts[0]->user->firstname;
With the join, the user's data is directly set on the post model, so I have to use it like so:
$posts[0]->firstname;
The thing is: I would like to use my User model because it has a few method inside that I'd like to use. One for printing the full name, one for printing its URL, etc..
What I am not able to do with eager loading, is to prevent a post from loading when it has no user attached to it. When the user associated with the post doesn't have the status 'active', I still get the post, and NULL for the user. But I don't want the post at all.
Is that something possible?
Am I being clear enough in my explications?
Thanks
You're overcomplicating things. Eloquent has everything you need:
// first make sure you load only posts of active users
$posts = Post::whereHas('user', function ($q) {
$q->where('status', 'active');
})
// then eager load the users
->with('user')
->get();
And by the way this is how you do, what you tried, in your join (w/o DB::raw):
$posts = Post::join('users', function($join){
// on() is for comparing fields
$join->on('users.id', '=', 'posts.user_id');
// while where is for comparing to provided values/strings
// just like simple query where()
$join->where('users.status', '=', 'active');
})

Laravel Eloquent: How to get only certain columns from joined tables

I have got 2 joined tables in Eloquent namely themes and users.
theme model:
public function user() {
return $this->belongs_to('User');
}
user model:
public function themes() {
return $this->has_many('Theme');
}
My Eloquent api call looks as below:
return Response::eloquent(Theme::with('user')->get());
Which returns all columns from theme (that's fine), and all columns from user (not fine). I only need the 'username' column from the user model, how can I limit the query to that?
Change your model to specify what columns you want selected:
public function user() {
return $this->belongs_to('User')->select(array('id', 'username'));
}
And don't forget to include the column you're joining on.
For Laravel >= 5.2
Use the ->pluck() method
$roles = DB::table('roles')->pluck('title');
If you would like to retrieve an array containing the values of a single column, you may use the pluck method
For Laravel <= 5.1
Use the ->lists() method
$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:
You can supply an array of fields in the get parameter like so:
return Response::eloquent(Theme::with('user')->get(array('user.username'));
UPDATE (for Laravel 5.2)
From the docs, you can do this:
$response = DB::table('themes')
->select('themes.*', 'users.username')
->join('users', 'users.id', '=', 'themes.user_id')
->get();
I know, you ask for Eloquent but you can do it with Fluent Query Builder
$data = DB::table('themes')
->join('users', 'users.id', '=', 'themes.user_id')
->get(array('themes.*', 'users.username'));
This is how i do it
$posts = Post::with(['category' => function($query){
$query->select('id', 'name');
}])->get();
First answer by user2317976 did not work for me, i am using laravel 5.1
Using with pagination
$data = DB::table('themes')
->join('users', 'users.id', '=', 'themes.user_id')
->select('themes.*', 'users.username')
->paginate(6);
Another option is to make use of the $hidden property on the model to hide the columns you don't want to display. You can define this property on the fly or set defaults on your model.
public static $hidden = array('password');
Now the users password will be hidden when you return the JSON response.
You can also set it on the fly in a similar manner.
User::$hidden = array('password');
user2317976 has introduced a great static way of selecting related tables' columns.
Here is a dynamic trick I've found so you can get whatever you want when using the model:
return Response::eloquent(Theme::with(array('user' => function ($q) {
$q->addSelect(array('id','username'))
}))->get();
I just found this trick also works well with load() too. This is very convenient.
$queriedTheme->load(array('user'=>function($q){$q->addSelect(..)});
Make sure you also include target table's key otherwise it won't be able to find it.
This Way:
Post::with(array('user'=>function($query){
$query->select('id','username');
}))->get();
I know that this is an old question, but if you are building an API, as the author of the question does, use output transformers to perform such tasks.
Transofrmer is a layer between your actual database query result and a controller. It allows to easily control and modify what is going to be output to a user or an API consumer.
I recommend Fractal as a solid foundation of your output transformation layer. You can read the documentation here.
In Laravel 4 you can hide certain fields from being returned by adding the following in your model.
protected $hidden = array('password','secret_field');
http://laravel.com/docs/eloquent#converting-to-arrays-or-json
On Laravel 5.5, the cleanest way to do this is:
Theme::with('user:userid,name,address')->get()
You add a colon and the fields you wish to select separated by a comma and without a space between them.
Using Model:
Model::where('column','value')->get(['column1','column2','column3',...]);
Using Query Builder:
DB::table('table_name')->where('column','value')->get(['column1','column2','column3',...]);
If I good understood this what is returned is fine except you want to see only one column. If so this below should be much simpler:
return Response::eloquent(Theme::with('user')->get(['username']));
#You can get selected columns from two or three different tables
$users= DB::Table('profiles')->select('users.name','users.status','users.avatar','users.phone','profiles.user_id','profiles.full_name','profiles.email','profiles.experience','profiles.gender','profiles.profession','profiles.dob',)->join('users','profiles.user_id','=','users.id')
->paginate(10);
Check out, http://laravel.com/docs/database/eloquent#to-array
You should be able to define which columns you do not want displayed in your api.

Categories