Laravel 4 - How to use where conditions for relation's column - laravel

This is what I want, I have two tables. one is 'Restaurants' and other is 'Facilities'.
The tables are simple.. and One-To-One relations. like there is a restaurant table with id, name, slug, etc and another table called facilities with id, restaurant_id, wifi, parking, etc
Here are my models:
class Restaurant extends Eloquent {
protected $table = 'restaurants';
public function facilities() {
return $this->hasOne('Facilities');
}
}
class Facilities extends Eloquent {
protected $table = 'facilities';
public function restaurant() {
return $this->belongsTo('Restaurant');
}
}
I want do like this Select * from restaurants r left join facilities rf on r.id=rf.restaurant_id where r.name = 'bbq' and rf.wifi != '1'.
How to use Eloquent to do that?
ps. sorry for modify from https://stackoverflow.com/questions/14621943/laravel-how-to-use-where-conditions-for-relations-column#= , but I have the similar problem.

You can use where and other sql-based methods on the relationship objects.
That means you can either create a custom method in your model:
class Restaurant extends Eloquent {
protected $table = 'restaurants';
public function facilities($wifi) {
return $this->belongsTo('Facility')->where('wifi', '=', $wifi);
}
}
Or you can try to use query scopes:
class Restaurant extends Eloquent {
protected $table = 'restaurants';
public function facility() {
return $this->belongsTo('Restaurant');
}
public function scopeFiltered($query, $wifi)
{
return $query->where('wifi', '>', 100);
}
}
Then:
$wifi = 1;
$restaurants = Restaurant::facilities()->filtered($wifi)->get();
This isn't exactly what you need likely, but query scopes is likely what you want to use to get what you're attempting.
THe key point is to know that relationship classes can be used like query builders - for example:
$this->belongsTo('Facility')->where('wifi', '=', $wifi)->orderBy('whatever', 'asc')->get();

There are some ways to filter both, this is using QueryBuilder:
Restaurant::join('facilities','facilities.restaurant_id','=','restaurants.id')
->where('name','bbq')
->where('facilities.wifi','!=', 1)
->get();

Related

How to sort parent data based on child column detial in laravel 5.4?

I have 3 way relationship firstl i have get code like this in a controller
Trial::with('subjects')->where('source_id', $sourceId)->get()->toArray()
Now I want to sort subject.reactions on desc order of subject.reactions.accounts.nb_followers column. I tried to use orderby on relationship but it does not work because it sorting account indsted on reactions. I want to sort reaction based on value of "nb_followes" column present inside account table.
Trail Model
class Trial extends Model
{
use HasFactory;
public $table = 'trials';
public function subjects()
{
return $this->hasMany(Subject::class, 'trial_id')->with(['reactions', 'news'])->withCount('reactions');
}
}
Subject Model
class Subject extends Model
{
use HasFactory;
public $table = 'subjects';
public function reactions()
{
return $this->hasMany(Reaction::class, 'subject_id', 'id')->with(['accounts' => function ($q) {$q->orderBy('nb_followers', 'DESC');}])->where('twitter_error', 0)->where('active', 1)->orderby('key_reaction', 'DESC');
}
public function news()
{
return $this->hasone(News::class, 'id', 'news_item_id');
}
Reaction Model
class Reaction extends Model
{
use HasFactory;
public $table = 'reactions';
public function accounts()
{
return $this->belongsTo(Account::class, 'account_id', 'id')->where('name', '!=', '');
}
Thank you in Advance.
I want to sort reactions based on account table's column yes i cant use simple eloquent query because it will not create a structure that i want so that's why i created these relationships.
first you need to loop over trails using map method
second you need to use transform method to transform your subject in
sorted manner
as show in Subject model you need sort reaction by key_reaction
feild
$trails = ViewTrial::with('subjects')->where('source_id', $sourceId)->get();
$trails->map(function($trails){
return $trails->subjects = $trails->subjects->transform(function (&$subject) {
return [
"column" => $subject->column,
"reactions" => $subject->reactions->sortByDesc(function ($reactions) {
return $reactions['accounts']['nb_followers'];
})->sortByDesc('key_reaction')->values()->all()
]
})->values();
});
return $trails->toArray();

Laravel Eloquent How Can I Select Using Condition "where" for pivot table

I have three database tables called user(id,name), group(id,name) and user_group(user_id, group_id,valid_before) with relations many to many.
class User extends Model
{
protected $table = 'user';
public function groups()
{
return $this->belongsToMany(Group::class, 'user_group')
->withPivot('valid_before');
}
}
class Group extends Model
{
protected $table = 'group';
public $timestamps = false;
public function user()
{
return $this->belongsToMany(User::class, 'user_group');
}
}
How can I select all users (using Eloquent) who have
valid_before < $some_date
?
There are many ways to achieve this goal. I'll show you an example using query scopes.
In your User class you have to make a little update:
class User extends Model
{
protected $table = 'user';
public function groups()
{
return $this->belongsToMany(Group::class, 'user_group')
//->withPivot('valid_before'); <-- Remove this
}
}
and create a scope in your Group model:
class Group extends Model
{
protected $table = 'group';
public $timestamps = false;
public function user()
{
return $this->belongsToMany(User::class, 'user_group');
}
/**
* This scope gets as input the date you want to query and returns the users collection
*
* #param \Illuminate\Database\Eloquent\Builder $query
* #param string $date
* #return \Illuminate\Database\Eloquent\Builder
*/
public function scopeUsersValidBefore($query, $date)
{
return $query->users()->wherePivot('valid_before', '<', $date);
}
}
Now, I imagine you have a GroupController that somewhere creates a query to retrieve the valid before users. Something like:
// [...]
$users = Group::usersValidBefore($yourDate)->get();
// [...]
If you want to create the query from the other side, I mean you want to use the User model and list all the Users that has a pivot relation with valid_before populated, than the right approach is creating a UserGroup intermediate model that can be easily used to create a query.
If you are using Laravel 8.x.x
It's much easier with Inline Relationship Existence Queries
If you would like to query for a relationship's existence with a single, simple where condition attached to the relationship query, you may find it more convenient to use the whereRelation and whereMorphRelation methods. For example, we may query for all posts that have unapproved comments:
use App\Models\Post;
$posts = Post::whereRelation('comments', 'is_approved', false)->get();
Of course, like calls to the query builder's where method, you may also specify an operator:
$posts = Post::whereRelation(
'comments', 'created_at', '>=', now()->subHour()
)->get();

How to join two or more table using laravel eloquent method?

Little confused about eloquent-relationship joins, since I used to get the result by query builder so far. Referred with other related questions still, I am not clear. Please explain me with a better example.
Model 1 - customer
Model 2 - customer_items (relating customer_id and item_id)
Model 3 - items (details about items)
Now I want to list the item details that customer related to.
join customer_items with items where customer.id = customer_items.user_id and items.id = customer_items.item_id.
First define methods in models.
Customer.php // Customer model
class Customer extends Model
{
protected $table = 'customers';
protected $primaryKey = 'customer_id';
public function customerItems(){
//customer_id is a foreign key in customer_items table
return $this->hasMany(Item::class, 'customer_id');
// A customer will has many items thats why hasMany relation is used here
}
}
CustomerItem.php // CustomerItem
class CustomerItem extends Model
{
protected $table = 'customer_items';
protected $primaryKey = 'customer_item_id';
public function itemDetail(){
//customer_id is a foreign key in customer_items table
return $this->hasOne(Customer::class, 'customer_item_id');
//A Item will has single detail thats why hasOne relation used here
}
}
In CustomerController.php
use \Customer // define customer model path
public function getCustomerItem(Request $request)
{
// Eloquent query to get data
$customer_item_detail_data = Customer::with('customerItems.itemDetail')->get();
//it return all items of customers with item details
//$customer_item_detail_data = Customer::with('customerItems')->with('customerItems.itemDetail')->get(); you can also use in this way
}
Hope it helps. Thank you.
First, you would need to define your models as such:
class Customer extends Model
{
protected $table = 'customers';
public function items(){
return $this->hasMany(Item::class, 'customer_id');
}
}
class CustomerItem extends Model
{
protected $table = 'customer_items';
public function customer(){
return $this->belongsTo(Customer::class, 'customer_id');
}
}
Then you would call the the relationship as such:
$customer = Customer::find(1); // This will get the first customer in the DB
$itemsOfCostumer = $customer->items // This will return all the items of the customer
// Now let suppose we have an ItemCustomer and we would like to know the owner
$customerItem = CustomerItem::find(1); // Get the first item of a customer in DB
$customer = $customerItem->customer; // Ther you have the customer
This is just a small example. Stackoverflow is not an educational website which I would highly advise you to visit Laravel Relationship Docs. Over there you can learn much more and they have a really good series at Laracast about relationships (if you are visual learner) https://laracasts.com/series/eloquent-relationships
If I got well your question, you are looking for a query to get the item details.
$item_details = Items::
join('customer_items', 'customer_items.item_id', '=', 'items.id')
->join('customer', 'customer.id' '=', 'customer_items.customer_id');
Or you can get the same result doing:
$item_details = DB::table('items')
->join('customer_items', 'customer_items.item_id', '=', 'items.id')
->join('customer', 'customer.id' '=', 'customer_items.customer_id');

Eloquent Relationships Optimize Query

I have the following models: CardBoard, User, UserPricingPlans, PricingPlanLimits
Note: Don't mind if there is something wrong with the models code.They are working fine.
CardBoard
class CardBoard extends Model{
public function user(){
return $this->belongsTo('Models\User','id_user');
}
}
User
class User extends Model{
public function pricingPlans(){
return $this->hasMany('Models\UserPricingPlan','id_user');
}
}
PricingPlan
class PricingPlan extends Model{
public function limits(){
return $this->hasOne('Models\PricingPlanLimits','id_pricing_plan','id_pricing_plan');
}
}
PricingPlanLimits
I'll not describe that Model, its not necessary for the problem. But keep in mind that there is an attribute called maxBoards.
The problem is that I only have the CardBoard Model Instance to work on and I want to get the maxBoard attribute from PricingPlanLImits. So I did it like this:
Note: I Already have the CardBoard Model Instance here!
$maxBoard = $cardBoard->user->pricingPlans->last()->limits->maxBoard;
return $maxBoard;
The code above runs great, but the number of queries generated by this operation is an overhead to me. Eloquent do an SELECT for every Relationship called and I don't want all these data and operations.
{
"query": "select * from `users` where `users`.`id_user` = ? limit 1",
"bindings": [
],
"time": 0.96
}
{
"query": "select * from `users_princing_plan` where `users_princing_plan`.`id_user` = ? and `users_princing_plan`.`id_user` is not null",
"bindings": [
],
"time": 0.8
}
{
"query": "select * from `pricing_plan_limits` where `pricing_plan_limits`.`id_pricing_plan` = ? and `pricing_plan_limits`.`id_pricing_plan` is not null limit 1",
"bindings": [
],
"time": 0.88
}
Isn't there an way to optmize this and run fewer queries in a Eloquent-Way ?
you can get a data in one query if you use with() method.
for example: CardBoard::with('user.pricingPlans')->get();
so can optimize your query using with method.
Previous comments were not too relevant to this solution...
example
$cardboard->user()->whereHas('pricingPlans', function ($plans) {
$plans->selectRaw('price_plan_limits.id, MAX(price_plan_limits.maxBoard) as MB'))
->from('price_plan_limits')
->where('price_plan_limits.id', 'price_plan.id')
->orderBy('MB', 'DESC')
})->get();
I usually go in reverse order:
$maxBoard = PricingPlanLimits::whereHas(function($q)use($cardBoard){
$q->whereHas('PricingPlan', function($q1)use($cardBoard){
$q1->whereHas('User', function($q2)use($cardBoard){
$q2->whereHas('CardBoard', function($q3)use($cardBoard){
$q3->where('id', $cardBoard['id']);
});
});
// Probably you have to improve this logic
// It is meant to select select the last occurrence
$q1->orderBy('created_at', 'desc');
$q1->limit(1);
});
})->first()['maxBoard'];
Totally untested, but this should be the correct approach to achieve your goal in one query.
You probably could reduce the number of calls by using hasManyThrough relation (See:https://laravel.com/docs/5.4/eloquent-relationships#has-many-through).
In that case you'd have something like
class CardBoard extends Model{
public function userPricingPlans(){
return $this->hasManyThrough('Models\UserPricingPlan', 'Models\User', 'id_user', 'id_user');
}
}
And then you could call it like this:
$maxBoard = $cardBoard->userPricingPlans->last()->limits->maxBoard;
To have it all in a single query you'd need fluent and real SQL joins, can't be done with eloquent (but then you'll miss all of ORM fun)
Typically it is totally fine to achieve the result with 3 queries, a query like that normally take 10ms. But each of your queries is taking nearly 1 second, which is way too long. I don't know the reason why.
You are also able to achieve the same result with a single query though.
Your naming is a bit unconventional. I use a more popular naming convention, hopefully you can apply to your case.
class CardBoard extends Model
{
protected $table = 'card_boards';
public function user()
{
return $this->belongsTo(User::class,'user_id');
}
}
class User extends Model
{
protected $table = 'users';
public function pricingPlans()
{
return $this->hasMany(UserPricingPlan::class,'user_id');
}
}
class PricingPlan extends Model
{
protected $table = 'pricing_plans';
// This is a one to one relationship so I use a singular form.
public function limit()
{
return $this->hasOne(PricingPlanLimit::class,'pricing_plan_id');
}
}
class PricingPlanLimit extends Model
{
protected $table = "pricing_plan_limits";
}
The query to get the result:
$carboardId = 100;
$latestPricingPlanSubQuery = PricingPlan::select('pricing_plans.*', DB::raw('MAX(created_at) as last_post_created_at'))
->groupBy('user_id');
$carboard = Carboard::select('card_boards.*', 'pricing_plan_limits.max_board')
->join('users', 'cardboards.user_id', '=', 'users.id')
->joinSub($latestPricingPlans, 'latest_pricing_plans', function ($join){
$join->on('users.id', '=', 'latest_pricing_plans.user_id');
})
->join('pricing_plan_limits', 'latest_pricing_plans.id', '=', 'pricing_plan_limits.pricing_plan_id')
->find($cardboardId);
The key thing is to have a sub query getting only the latest pricing plans for each user.

Self join in Laravel

I am trying to build a self join in Laravel. I wish to compare 2 columns in the same table.
When I write this in my controller:
$char_name_obj = User::find($user->id)->characters()->where('lord_id', '=', 'id')->get();
lord_id is one column, id is the other column in the same table named characters. This returns nothing. I am sure I need to do a self join to be able to achieve this.
Character model:
class Character extends Eloquent {
protected $table = 'characters';
protected $fillable = array('lord_id','char_name', 'char_dynasty', 'picture');
public function user()
{
return $this->belongsTo('User');
}
public function Titles()
{
return $this->hasMany('Title');
}
public function LordCharID()
{
return $this->has_one('Character');
}
}
I don't know how to use the last function LordCharID().
has_one is the Laravel 3 function; so if you're using Laravel 4, you need to use hasOne
You can use whereRaw to compare columns:
User::find($user->id)->characters()->whereRaw('lord_id = id')->get();

Resources