Laravel load only Model - laravel

I want to load only the model to be able to add wheres etc later.
$query = Users;
if($request->filter == 1) {
$query = $query->where('user_access', 'admin');
}
if($request->otherFilter == 1) {
$query = $query->where('user_email', '');
)
$query->get();
Although using $query = Users; doesn't work..?

You can use it by creating a Illuminate\Database\Eloquent\Builder instance as:
$query = Users::query();
and then you can add further queries in query builder instance.

You can just create an instanse of the Users model with:
$query = new Users;
And then work with it.

You should really consider using local query scopes with Eloquent if you are planning to do lots of different queries.
Laravel Eloquent Query Scopes
//App\User.php
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}
$users = App\User::popular()->orderBy('created_at')->get();
You can create custom queries instead of long verbose ones.

Related

Cache eloquent model with all of it's relations then convert it back to model with relations?

I am trying to optimize a project that is working pretty slow using caching. I'm facing a problem that I don't quite understand how to cache full eloquent models with their relationships and later on covert them back to a model with all relations intact. Here's a fragment of my code
if (Cache::has($website->id.'_main_page')) {
$properties = (array) json_decode(Cache::get($website->id.'_main_page'));
$page = Page::hydrate($properties);
}else{
$expiresAt = now()->addMinutes(60);
$page = Page::with(['sections', 'pageSections.sectionObjects'])->where('website_id', $website->id)->where('main', 1)->first();
Cache::put($website->id.'_main_page', $page->toJson(), $expiresAt);
}
Problem is, hydrate seems to be casting this data as a collection when in fact it's suppose to be a single model. And thus later on I am unable to access any of it's properties without getting errors that they don't exists. $properties variable looks perfect and I would use that but I need laravel to understand it as a Page model instead of stdClass, I also need all of the relationships to be cast into their appropriate models. Is this even possible? Any help is much appreciated
Is there any reason you can't use the cache like this
$page = Cache::remember($website->id.'_main_page', 60 * 60, function () use ($website) {
return Page::with(['sections', 'pageSections.sectionObjects'])
->where('website_id', $website->id)
->where('main', 1)
->first();
});
Conditional
$query = Page::query();
$key = $website->id.'_main_page';
if (true) {
$query = $query->where('condition', true);
$key = $key . '_condition_true';
}
$query::with(['sections', 'pageSections.sectionObjects'])
->where('main', 1)
$page = Cache::remember($key, 60 * 60, function () use ($query) {
return $query->first();
});

The equivalence of the AND statement in a ON statement (JOIN) in EloquentORM

Take a look at the query below:
SELECT v*, s.*
FROM videos v
INNER JOIN subtitles s ON (s.subtitle_id = v.video_id AND s.language = 'en')
WHERE v.video_id = 1000
I want to find the equivalent data retrieval action for a Laravel / Eloquent ORM environment.
So, my options are:
using the DB facade
using the query builder
defining the relationship in the Video model
Let's say I wish to use the latter (if possible).
namespace App\Models\v1;
use App\Models\v1\Subtitles;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
protected $table = 'videos';
public function subtitle()
{
return $this->hasOne(Subtitles::class, 'subtitle_id', 'video_id'); // How can I define the AND s.language = 'en' ?
}
}
The problem here is that I don't know how to define the AND s.language = 'en' in EloquentORM.
Any help is appreciated.
You can add a where clause to the relationship:
public function subtitle()
{
return $this->hasOne(Subtitles::class, 'subtitle_id', 'video_id')->whereLanguage('en');
}
Retrieving the model:
Provided you changed your primaryKey property on the video model:
protected $primaryKey = 'video_id';
Docs
You can do the following:
$video = Video::findOrFail(1000);
$subtitle = $video->subtitle;
You can define the relationship and then use whereHas
public function subtitle()
{
return $this->hasOne(Subtitles::class, 'subtitle_id', 'video_id');
}
And then filter it like this
$video = Video::where('video_id', 1000)->whereHas('subtitle', function($query){
$query->where('language', 'en');
})->first();
For details check the doc
If you just want to use join then you can use it like this
$lang = 'en';
$video = Video::where('video_id', 1000)
->join('subtitles', function ($join) use ($lang){
$join->on(function ($query) use ($lang) {
$query->on('subtitles.subtitle_id', '=', 'videos.video_id');
$query->on('subtitles.language', '=', DB::raw($lang));
});
})->first();
Check laravel join

Laravel Eloquent: orderBy related table

I would like to order result of eloquent by field on the other related table.
I have users table. Every user has one profile. Profile has sponsored (which is boolean) field. So when I would like to get all users, I want to display first sponsored users, then non sponsored.
public function profile(){
return $this->hasOne('App\Doctor');
}
There are two ways:
1)You have to join tables,
User::join('profiles','users.id','=','profile.user_id')->orderBy('sponsored','DESC')->get()
2)Order by eager loading
User::with(array('profile' => function($query) {
$query->orderBy('sponsored', 'DESC');
}))
->get();
Try this one
User::leftJoin('profile', 'user.id', '=', 'profile.user_id')
->orderBy('profile.sponsored', 'ASC')
->get();
I highly recommend not using table joins as it would fail you on the scale.
A better solution is to get users, get their profiles and then sort them using laravel collection methods.
You can use this sample to achieve this solution.
//get all users
$users = User::all();
//extract your users Ids
$userIds = $users->pluck('id')->toArray();
//get all profiles of your user Ids
$profiles = Profile::whereIn('user_id', $userIds)->get()->keyBy('user_id');
//now sort users based on being sponsored or not
$users = $users->sort(function($item1, $item2) use ($profiles) {
if($profiles[$item1->id]->sponsored == 1 && $profiles[$item2->id]->sponsored == 1){
return 0;
}
if($profiles[$item1->id]->sponsored == 1) return 1;
return -1;
});
You can check this link which explains on laravel collection sorts.
$order = 'desc';
$users = User::join('profile', 'users.id', '=', 'profile.id')
->orderBy('profile.id', $order)->select('users.*')->get();

Join query using eloquent model mapping

I am trying to do this
select notifications.id, reservations.number from
notifications
JOIN reservations
ON notifications.reservation_id = reservations.id
WHERE notifications.status = 1
using eloquent so I have this this
$await = Notification::with('Reservation')->
select('notifications.id', 'reservations.number')
->where('notifications.status', '=', 1)->get();
return Response::json($awaitLists);
In my Notification model
public function Reservation() {
return $this->belongsTO('Reservation');
}
In my Reservation Model
public function notification() {
return $this->hasMany('Notification');
}
So notification belongs to reservation while reservation has a 1 to many relationship
My question is why can't what I have tried works. I keep getting Unknown column 'reservation.number' but i do have column called number in the reservations table. I know they is a way to use eloquent relationship mapper to do this.
This should do it:
$notifications = Notification::where('status','=',1)->get();
foreach($notifications as $notification) {
$id = $notification->id;
$num = $notification->reservation->number;
$await = [$id,$num];
var_dump($await);
}
The error you're seeing is because eager loading relationships doesn't actually perform a join. It uses two separate queries, and then the relationship fields are assigned after the queries are run.
So, when you do Notification::with('Reservation')->get(), it is running two SQL statements, approximately:
Notification::with('Reservation')->get();
// select * from notifications;
// select * from reservations where id in (?, ?, ...);
You can see the actual queries run with a dd(DB::getQueryLog()), if you're interested.
How you move forward depends on what you need to do. If you need to duplicate your existing query exactly, then you'll need to manually perform the joins.
$notifications = Notification::select('notifications.id', 'reservations.number')
->join('reservations', 'notifications.reservation_id', '=', 'reservations.id`)
->where('notifications.status', '=', 1)
->get();
foreach($notifications as $notification) {
print_r($notification->number);
}
Otherwise, you can just use the objects as they are built by Laravel:
$notifications = Notification::with('Reservation')->where('status', '=', 1)->get();
foreach($notifications as $notification) {
print_r($notification->Reservation->number);
}

Can you use WHERE logic to fetch data through a pivot table in Laravel?

Is it possible to turn the following code below into one line?
// Fetch the producer role
$Roles = Role::where('label', '=', 'Producer')->get();
$role = $Roles->first();
// Fetch the producers
$Producers = Role::find($role->role_id)->users()->get();
Also here is what my Pivot table logic looks like (inside Role):
public function users() {
return $this->belongsToMany('User', 'UsersRoles', 'role_id', 'user_id');
}
Yes, but why would you want to squash it to one line? Apart from being it completely redundant..
// 1st query, load collection of roles...
$Roles = Role::where('label', '=', 'Producer')->get();
// ... just to use one of them?
$role = $Roles->first();
$Producers =
// run another query to get the same role...
Role::find($role->role_id)
->users()->get();
Anyway, here's one-liner:
$producers = Role::where('lable', 'Producer')->first()->users;
// or
$producers = Role::where('lable', 'Producer')->with('users')->first()->users;

Resources