Laravel Eloquent Joins use of belongs to - laravel

I have 2 table for get inner join query records in laravel eloquent, but is not working with combine data get.
Table :category
id
name
cat_status
Table:image
id
title
desc
img_status
image controller
//get all active images
ImageModel::where('status',true)->get();
public function getCategory() {
return $this->belongsTo('\App\Api\CategoryModel','cat_id');
get category active only
public function getActiveCategory() {
return $this->getCategory()->where('cat_status','=', 1);
}
I need to get only which category is active, that images only.
if cat 1,2,3 3(is inactive)
image table
title=a1,cat_id=1,
title=a2,cat_id=2,
title=a3,cat_id=3
Now I get all 3 images , i need only first 2, because cat_id 3 is inactive .
Any ideas how I can join the status condition.
Thanks in advance.

Well relation methods doesn't provides joins. You have to do it manually.
If You want to get IMAGES from active categories then You''ll have to query like this:
// method from image model
public function getImagesFromActiveCategory() {
return $this->join('category')
->select('image.*')
->join('category', 'category.id', '=', 'image.cat_id')
->where('cat_status','=', 1)
->get();
}

Thank you for your giving the quick responses,
I tried to use other but is solved now with the use of belongsToMany Method.
Below functions gives exact records from the DB, which match with each category and images table, and category status active records only.
#Image Controller
$images= ImageModel::where('status',true)->get();
foreach($images as $img){
#get data
$onlyactiveCategoryImages=$img->getActiveCategory()->get();
}
#image Model
#get category data
public function getCategory() {
return $this->belongsToMany('\App\Api\CategoryModel','images','id','cat_id');
}
#get category active only
public function getActiveCategory() {
return $this->getCategory()->where('cat_status','=', 1);
}
Thank you once again for all :).

Related

Laravel eloquent for four tables

I'm new to Laravel. I am developing a project. and in this project I have 4 tables related to each other
-Users
-Orders
-OrderParcels
-Situations
When listing the parcels of an order, I want to get the information of that order only once, the user information of that order once again, and list the parcels as a table under it. so far everything ok. but I also want to display the status of the parcels listed in the table as names. I couldn't add the 4th table to the query. do you have a suggestion? I'm putting pictures that explain the structure below.
My current working code is
$orderParcels = Orders::whereId($id)
->with('parcels')
->with('users:id,name')
->first();
and my 'orders' model has method
public function parcels(){
return $this->hasMany(OrderParcels::class);
}
public function users(){
return $this->hasOne(User::class,'id','affixer_id');
}
Note[edit]: I already know how to connect like this
$orderParcels = DB::table('order_parcels as op')
->leftjoin('orders as o','op.orders_id','o.id')
->leftjoin('users as u','o.affixer_id','u.id')
->leftjoin('situations as s','op.status','s.id')
->select('op.*','o.*','u.name','s.situations_name')
->where('op.orders_id',$id)->get();
but this is not working for me, for each parcels record it returns me orders and user info. I want once orders info and once user info.
Laravel provides an elegant way to manage relations between models. In your situation, the first step is to create all relations described in your schema :
1. Model Order
class User extends Model {
public function parcels()
{
return $this->hasMany(OrderParcels::class);
}
public function users()
{
return $this->hasOne(User::class,'id','affixer_id');
}
}
2. Model Parcel
class Parcel extends Model {
public function situations()
{
return $this->hasOne(Situation::class, ...);
}
}
Then, you can retrieve all desired informations simply like this :
// Retrieve all users of an order
$users = $order->users; // You get a Collection of User instances
// Retrieve all parcels of an order
$parcels = $order->parcels; // You get a Collection of User instances
// Retrieve the situation for a parcel
$situations = $parcel->situations // You get Situation instance
How it works ?
When you add a relation on your model, you can retrieve the result of this relation by using the property with the same name of the method. Laravel will automatically provide you those properties ! (e.g: parcels() method in your Order Model will generate $order->parcels property.
To finish, in this situation where you have nested relations (as describe in your schema), you should use with() method of your model to eager load all the nested relation of order model like this :
$orders = Orders::with(['users', 'parcels', 'parcels.situations'])->find($id)
I encourage you to read those stubs of Laravel documentation :
Define model relations
Eager loading
Laravel Collection
Good luck !
Use join to make a perfect relations between tables.
$output = Orders::join('users', 'users.id', '=', 'orders.user_id')
->join('order_parcels', 'order_parcels.id', '=', 'orders.parcel_id')
->join('situations', 'situation.id', '=', 'order_parcels.situation_id')
->select([
'orders.id AS order_id',
'users.id AS user_id',
'order.parcels.id AS parcel_id',
'and so on'
])
->where('some row', '=', 'some row or variable')->get();

Get only one column from relation

I have found this: Get Specific Columns Using “With()” Function in Laravel Eloquent
but nothing from there did not help.
I have users table, columns: id , name , supplier_id. Table suppliers with columns: id, name.
When I call relation from Model or use eager constraints, relation is empty. When I comment(remove) constraint select(['id']) - results are present, but with all users fields.
$query = Supplier::with(['test_staff_id_only' => function ($query) {
//$query->where('id',8); // works only for testing https://laravel.com/docs/6.x/eloquent-relationships#constraining-eager-loads
// option 1
$query->select(['id']); // not working , no results in // "test_staff_id_only": []
// option 2
//$query->raw('select id from users'); // results with all fields from users table
}])->first();
return $query;
In Supplier model:
public function test_staff_id_only(){
return $this->hasMany(User::class,'supplier_id','id')
//option 3 - if enabled, no results in this relation
->select(['id']);// also tried: ->selectRaw('users.id as uid from users') and ->select('users.id')
}
How can I select only id from users?
in you relation remove select(['id'])
public function test_staff_id_only(){
return $this->hasMany(User::class,'supplier_id','id');
}
now in your code:
$query = Supplier::with(['test_staff_id_only:id,supplier_id'])->first();
There's a pretty simple answer actually. Define your relationship as:
public function users(){
return $this->hasMany(User::class, 'supplier_id', 'id');
}
Now, if you call Supplier::with('users')->get(), you'll get a list of all suppliers with their users, which is close, but a bit bloated. To limit the columns returned in the relationship, use the : modifier:
$suppliersWithUserIds = Supplier::with('users:id')->get();
Now, you will have a list of Supplier models, and each $supplier->users value will only contain the ID.

Laravel, many-to-many relationship among multiple models

I Have multiple models that have many to many relationship
Here are the models
News Section
Categories
Sub Categories
Approved News
Pending News
Each News Section can have multiple Categories.
Each Category can have multiple Sub Categories.
Each Sub Category can has multiple Approved News and Pending News.
I want to have News with Categories, Sub Categories and Pending / Approve news
and stuff like
Categories with Sub Categories and Approve news
I tried with pivot tables but not able to get results
Models are as follow
News Section
class NewsSection extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
}
Category
class Category extends Model
{
public function subcats(){
return $this->belongsToMany(SubCategory::class);
}
public function newssections(){
return $this->belongsToMany(NewsSection::class);
}
}
SubCategory
class SubCategory extends Model
{
public function category(){
return $this->belongsTo(Category::class);
}
public function approvednews(){
return $this->belongsToMany(ApprovedNews::class);
}
public function pendingnews(){
return $this->belongsToMany(PendingNews::class);
}
}
ApprovedNews
class ApprovedNews extends Model
{
public function subcategories (){
return $this->belongsToMany(SubCategory::class);
}
}
PendingdNews
class PendingdNewsextends Model
{
public function subcategories (){
return $this->belongsToMany(SubCategory::class);
}
}
Update
This what I have done so far
$news =Category::with('subcats.approvednews')->where('id',1)->get();
I got all the approved news with subcategories and categories
how can i modify this to get specific subcats and approved news per category, if i do this
$news =Category::with('subcats.approvednews')->where('subcats.id',1)->get();
I get an error like id ambiguous
Is it possible to pick and chose items from relation for instance return just 2 subcats and 3 approved news for each subcat of selected category
or
get count of approved news and pending news per subcat and category
Thanks in advance
The error "error like id ambiguous" means that you need to specify the table in your where('id', 1) like where('table.id', 1) so that MySQL knows which id column in which table you mean.
You can constrain the models returned by with like this:
Category::with(['subcats' => function(Builder $query) {
$query->where('id', '=', 1);
}]);
Also you can count relations:
$subcat = SubCategory::withCount(['approvednews']);
$subcat->approvednews_count;
Limiting eager loaded relations is not possible per the docs.
A workaround may be to go the other way round starting from ApprovedNews:
ApprovedNews::whereHas(['subcategories' => function(Builder $query) {
$query->where('id', '=', 1);
}])->limit(10);
I have a few suggestions of how you can get this to work. In your comments, you say you are getting an issue doing the following:
$items=Category::with('subcategory')->where('id',1)->get();
Where is 'subcategory' coming from? By the looks of your model, your relationship between Category and Subcategory is called subcats. So you would need to change it to:
$items=Category::with('subcats')->where('id',1)->get();
And if you dump that out, you should see that you will get the category where the ID is 1, and the subcategories loaded in. A way to test that your relationships are working would be something like this:
$category = Category::find(1);
$subCats = $category->subcats()->get();
dd($subCats);
In your relationships, instead of using SubCategory::class I would suggest trying return $this->belongsToMany('App\SubCategory'); so that the models are definitely connected.
Once you have tested that your relationships between one another work, you can get started on testing that you can go from a->b->c etc.
May be using "Nested Eager Loading" and "scope", you can do something like
$pendings = NewSection::with('categories.subCategories')->pending()->get()
$approved = NewSection::with('categories.subCategories')->approved()->get()
not tested it, but you can try, may be with some modification, you can reach to your goal.
if you want return one collection, you may like to merge it
$approved->merge($pendings);
but, you should avoid it.

Count from hasManyThrough relationship with eager loading (need only Count)

I only need the count, don't want to retrive the results or perform query each time for each row. This is why I want eager loading.
I have 3 tables like the following:
Admins
id
Posts
id
admin_id
Comments
id
user_id //nullable (null if comment from admin)
admin_id //nullable (null if comment from user)
news_id
Now I want to retrieve all the posts from a single admin and all the comments count from those posts, without retrieving all the comments for posts, ONLY COUNT of comments,
With eager loading to avoid n+1 query issue;
Here I think we should make a relation to be used with eager loading like the following:
//admin model
public function commentsCountRelation()
{
return $this->hasManyThrough(Comment::class, News::class, 'admin_id', 'news_id')
->selectRaw('news_id, count(*) as count')
->groupBy('news_id');
}
--Look Here I used hasManyThrough relation because, news_id is not in Admins table.
Then I should make an attribute, to access the count easyly, like:
public function getCommentsCountAttribute()
{
return $this->commentsCountRelation->first()->count ?? 0;
}
Then access it like:
$admin = Admin::with('commentsCountRelation')->findOrFail($id);
$admin->commentsCount;
But it always returns null, why is that?
The following works for hasMany & belongsTo, I've used it on my other models like:
//relation
public function ordersCountRelation()
{
return $this->hasOne(Order::class)->selectRaw('user_id, count(*) as count')
->groupBy('user_id');
}
//attribute
public function getOrdersCountAttribute()
{
return $this->ordersCountRelation->count ?? 0;
}
//Then accessed like:
$user = User::with('ordersCountRelation')->find($id);
$user->ordersCount; //return only count
Any help will be highly appreciated
there is no need for using hasManyThrough.
just use some relations and withCount method:
$admin->posts->withCount('comments')->get();
then you can access it with: $comments_count

addGlobalScope withCount relationship's relationship

I have a 3 models: Phone, Product and Store.
A phone has many Product which belongs to a Store.
I am trying to add a golbal scope so that every time I load a phone, products and stores counts are loaded automatically.
products_count works fine, however stores_count is a bit tricky since store is not a Phone's relationship but a Product's.
I have tried the following but it gives me an error "Method getRelated does not exist.", I assume because stores() now returns a collection.
Any ideas as to how I could add the stores_count?
public static function boot(){
parent::boot();
static::addGlobalScope('products', function ($builder){
$builder->withCount('products');
$builder->withCount('stores'); <----- gives error
});
}
public function products(){
return $this->hasMany(Product::class);
}
public function stores(){
$store_ids = $this->products()->get(['store_id'])->unique();
return Store::find($store_ids);
}
Update after #Sandeesh answer.
I tried to use hasManyThrough but it returns an empty collection which is wrong.
When I dd($phone->products); I can see 7 products which have 3 different stores.
public function stores(){
return $this->hasManyThrough(Store::class, Product::class,
'store_id', 'id');
}
Database schema
Phone
-id
Product
-id
-phone_id
-product_id
-store_id
Store
-id
Update 2
So I managed to get the produced query from the stores() method above.
select `phones`.*,
(select count(*) from `products` where `phones`.`id` = `products`.`phone_id`) as `products_count`,
(select count(*) from `stores` inner join `products` on `products`.`id` = `stores`.`id` where `phones`.`id` = `products`.`store_id`) as `stores_count`
from `phones` where `slug` = ? limit 1
The problem is in the third line. The query is messed up, not sure what is wrong with the relationship though.
You can using hasManyThrough
https://laravel.com/docs/5.4/eloquent-relationships#has-many-through
public function stores()
{
return $this->hasManyThrough(Store::class, Product::class);
}
Edit
This should give you what you need. But eager loading is always better
protected $appends = [
'productCount',
'storeCount'
];
public function getProductCountAttribute()
{
return $this->products()->count();
}
public function getStoreCountAttribute()
{
return Store::whereIn('id', $this->products()->pluck('store_id')->toArray())->count();
}

Resources