I am trying to add a feature to a website built with Laravel.
There is a table containing vote numbers and user. I want to get the total points a user has in a certain category. I do not have any PHP or Laravel experience but said I would give this a shot.
$votes1 = UserVotes::select ('select vote from user_votes where feedback_id = ? and feedback_type = 1', Auth::user()->id);
This should return an object containing the vote amount. I want to interrogate the the object to check if the vote number is above a certain amount and then do something based on that being the case or not.
if vote > 50{
//do stuff
}
foreach ($votes1 as $vote1) {
echo $vote1->vote;
}
The query should return 1. I have verified this by querying the database, so the problem is with my understanding of Laravel or php. What I am doing wrong?
You don't need to construct your own SQL statement; Eloquent will do that for you.
If your models are set up in the default way, your query would look something like:
$votes = UserVotes::where('feedback_id', Auth::user()->id)
->where('feedback_type', 1)
->get();
You can then iterate over that as normal.
Additionally, if there is a relationship set up with the user model you could do something like
$votes = Auth::user()->votes()
->where('feedback_type', 1)
->get();
Check out the documentation here: http://laravel.com/docs/4.2/eloquent
assuming UsersVotes extends Model, here's how you should do it:
UsersVotes::select('vote')->where('feedback_type', 1)->where('feedback_id', Auth::user()->id)-get();
Related
I have user and news table and then a middle table called news_user, the middle table determines which news has been seen by the user. I can easily get objects that have been seen but, but now I need to show the user objects that have not been seen.
I did a workaround with putting all the seen news id in an array and look for news where id differs from the array. but I don't consider it as the healthiest solution.
This is what I did:
$seenNewses = DB::table('news_user')->where('user_id', Auth::id())
->pluck('news_id')->toArray();
$notSeenNewses = News::whereNotIn('id', $seenKeepers)
->orderBy('id', 'asc')->first();
Is there a way I can do this with a single query?
P.S: I have seen similar questions but they got little confused. Any answer is appreciated.
First in your News Model you need to make relationship like this
public function users()
{
return $this->belongsToMany(News::class,'news_user','news_id','user_id');
}
Below command will give you all the news which are not seen by any users yet
$notSeenNewses = News::doesntHave('users')->get();
If you want to put any condition then also check for
$notSeenNewses = News::whereDoesntHave('users', function ($query) {
$query->where('YOUR CONDITION');
})->get();
It's not tested but you can modify as per your need.
I've found some query result really unexpected.
It's Laravel 5.2
We have following entity:
User with method:
public function roles() : BelongsToMany
{
return $this->belongsToMany(Role::class)->withPivot('timestamp');
}
Each User can have many roles, so we have also Role entity (but it doesn't matter much in my question) and pivot table user_role with timestamp field (and ids of course), because we hold information about time, when User achieved specific role.
I want to get all Users with theirs last assigned Role
When I create query (in User context in some repository):
$this->with(['roles' => function($query) {
$query->orderBy('timestamp', 'desc');
}])->all();
the result will contain Users with Roles entities inside itself ordered by timestamp - it's ok. But I want to retrieve only one last role inside each User entity not all ordered.
So...
$this->with(['roles' => function($query) {
$query->orderBy('timestamp', 'desc')->limit(1);
}])->all();
And then I retrieve Users but only User which achieved some Role for the very last time contains it! All the other Users have their roles field containing empty array.
Why ordering was performed on each Users relation separately, but when I added limit it behaved like a global limit for all.
It drives me crazy...
Thanks for advices.
EDIT
I've created lastRoles() method to get all Roles ordered desc. But all, retrieving one is impossible.
public function lastRoles() : BelongsToMany
{
return $this->BelongsToMany(Roles::class)->withPivot('timestamp')->latest('timestamp');
}
And for testing:
$users = (new User())->with('lastRoles')->get();
But now I must iterate over Users and invoke lastRoles() on each one:
foreach ($users as $user) {
var_dump($user->lastRoles()->get()->first()->name);
}
Then I retrieve names of latest Roles assigned to each User.
So... There is no way to do it in one query? This is the only way?
For this to work, you would need a helper function:
public function latestRole()
{
return $this->hasOne(Role::class)->withPivot('timestamp')->orderBy('timestamp', 'DESC');
}
And then:
$this->with('latestRole')->get();
Credits to this awesome article.
When you eager load a relationship with query constraint(s), the query will be run once to load all relationships, not each one individually. This is the expected behavior. Think about it, eager loading exists to turn many queries into one query in order to optimize performance. There is only one query executed, so your limit constraint will limit the entire result set, rather than on a per model basis.
To circumvent this, you could try creating another belongsToMany method that adds the desired limit constraint. The following code is untested:
public function lastRole() : BelongstoMany
{
return $this->belongsToMany(Role::class)
->withPivot('timestamp')
->orderBy('timestamp', 'desc')
->limit(1);
}
Assuming this works, you can then simply change the relationship method from roles to lastRole and remove your query constraint:
$this->with('lastRole')->all();
I have a situation to update the row based on id and return the complete row. I can do it in two queries but i want to do it in one query only. I have write this...
$result= DB::table($this->table)
->whereRaw($where['rawQuery'], $where['bindParams'] ? $where['bindParams'] : array())
->increment($updateField);
if($result){
return DB::table($updateTable)
->select('id')
->where('campaign_id',$where['bindParams'])
->where('date',date("Y-m-d"))
->get();
}else{
throw Exception("Error in fetching data");
}
I copied this from what you commented in your question:
No i want to return the id of the updated row or complete row if possible
If you want to return the ID of the just updated 'row' you're talking about. You can use Eloquent to accomplish this.
You can use a route like this:
Route::put('demo/{id}', Controller#update);
Then in your update function in your controller you can get the ID.
Find the model with $model = Model::find(id);
do things with the data you get.
Like $model->name = Input::get('name');
Then use $model->save();
After saving you can do $model->id;
Now you get back the ID about the row you just updated.
Refer back to this question:
Laravel, get last insert id using Eloquent
But any way it'll always be at least 2 queries (a SELECT and an UPDATE in MySQL, however you do it)
You can check Laravel Eloquent if you want a "cleaner" way to to this.
I have a collection called User, I also have an array containing two models Post relating to the User model.
The User collection contains a primary key id and each model in my Post collection I have a foreign key user_id.
I am currently executing the following:
foreach ($users as $user) {
foreach ($posts as $post) {
if ($post->user_id == $user->id) {
$user->posts->push($post);
}
}
}
This somewhat works, but not entirely because it pulls in all related posts instead of the recent two posts a user has made.
The array looks like the following:
My User schema looks like; with a hasMany relationship to Post:
You can load the posts associated to a User using with, something like
$user = User::with('posts')->find($id);
But your scenario sounds specifically collecting the latest two Post belonging to a User. To limit your results you can also use scopes.
Something like the following on your Post model would work:
public function scopeLatest($query, $latest = 2)
{
return $query->limit($latest);
}
Then collect these by:
// The user record.
$user = User::find($id);
// Latest 2 posts for this user.
$posts = $user->posts()->latest();
// Latest 5 posts for this user.
$posts = $user->posts()->latest(5);
However, should you with to load the latest 2 posts with the user in a single query - then you could make a new relation:
public function latestPosts()
{
return $this->hasMany(Post::class, 'post_id', 'id')
->orderBy('created_at', 'ASC')
->limit(2);
}
This would work in the following way:
// Load the user with the latest 2 posts.
$user = User::with('latestPosts')->find($userId);
// Access these using; this will be a Collection containing 2 `Post` records.
dd($user->latestPosts);
Basically with Eloquent, when you call $this->latestPosts Eloquent will run latestPosts() and hydrate the related records. Using with this hydration occurs with a single query and the relations are already defined.
The difference between the method latestPosts() and the property $latestPosts is simple.
The method will always return a specific Relation Collection allowing you to chain additional conditions;
So: $user->latestPosts()->get() is the same as $user->latestPosts.
You cannot use query constraints / eager loading to do this. Doing so will only work if you are retrieving the posts for one user. However, if you try to retrieve the posts for multiple users, it will fail because eager loading / query constraints will limit the related results as a whole. To understand, you have to look at the queries Eloquent generates. Lets take a look at an example where you only need one user's posts.
$user = User::with(['posts' => function($query) {
$query->limit(2);
}])->find(1);
In this example, we are getting a user with a primary key of 1. We also also retrieving his/her posts but limiting it so we only retrieve 2 posts. This works, and it will generate 2 queries similar to this:
select * from `users` where `users`.`id` = 1 limit 1
select * from `posts` where `posts`.`user_id` in (1) limit 2
Okay. Now, why doesn't this work if you try to get more than 1 user (or a collection of users)? For example:
$user = User::with(['posts' => function($query) {
$query->limit(2);
}])->get();
In this case, I changed find(1) to get(), and it will generate 2 queries like this:
select * from `users`
select * from `posts` where `posts`.`user_id` in (?, ?, ?, ... ?) limit 2
It's important to take a look at the second query. It's retrieving all the related posts, but at the end, you'll see that it has limit 2. In other words, it's limiting the entire related collection to only 2, which is why query constraints do not work for this.
Achieving this is actually pretty complex, but a fellow member (Jarek Tkaczyk) came up with a solution using MySQL variables, which you can find here: Laravel - Limit each child item efficiently
You can do this a bit simpler with https://laravel.com/docs/5.2/eloquent-relationships#eager-loading constraints.
Example: Users have many Dogs, but only take 2
$user = App\User::with(['dogs' => function ($query) {
$query->limit(2);
}])->find($user_id);
dump($user);
The anonymous constraining function would also have an orderBy in your case
I have a search form in my Laravel app that searches through a list of properties. My properties table has a Model already, and is connected to another Model/table called details, which contains things like if a certain property has a garden, garage etc, through a one-to-many relationship. When doing index or show stuff this works fine - I can get the details for a property using $property->details.
The search form has a field where the user can search for such details, and this is where I'm struggling. I don't just need to find the detail itself, I need to first see if anything present in the input matches anything in the Details table, then see if the foreign key (called prop_id) matches any of my Property id's, then include those properties in the results. Something like:
$query = Property::where('archived',0);
$query->details->where('text',$keyword);
$results = $query->get();
The reason this won't work is because details isn't a property of $query, but I'm not sure what to use as an alternative as calling Property::where() again would surely reset the whole query. I'm storing it all in a $query variable because all the fields in my form are optional, so I'm building the query based only on the inputs that have values in.
Bit stumped here, so if anyone has already tackled something like this, the tips would be appreciated. Cheers!
Use whereHas:
$query = Property::where('archived', 0)->whereHas('details', function ($q) use ($keyword)
{
$q->where('text', $keyword);
});
$results = $query->get();
You may try something like this:
$query = Property::where('archived', 0);
if(isset($keyword)) {
$query->whereHas('details', function ($q) use ($keyword) {
$q->where('text', $keyword);
});
}
$results = $query->get();