Duplicated records in Yii2 GridView - php

It was working just fine, but I had to import data afresh in the existing order1 table which is related to order_item table on order.id = order_item.order_id and order_item.location_id = location.id
So to get the location in GridView of order1 table I had relation defined like so:
public function getLocation() {
return $this->hasOne(Location::className(), ['id' => 'location_id'])->viaTable('{{%order_item}}', ['order_id' => 'id']);
}
Now I have multiple records in the GridView. The query formed for the GridView is like:
SELECT `order1`.*
FROM `order1`
LEFT JOIN `order_item` ON `order1`.`id` = `order_item`.`order_id`
LEFT JOIN `location` ON `order_item`.`location_id` = `location`.`id`
where 1 ORDER BY `id` DESC
LIMIT 20;
How I can fix this as inner join or otherwise, so that it returns records only once from order1 table?
In GridView I am using location.location_title.
Note: there are multiple order items per order.
also Tried:
public function getOrderItem()
{
return $this->hasMany(OrderItem::className(), ['order_id' => 'id']);
}
public function getLocation()
{
return $this->hasOne(Location::className(), ['id' => 'location_id'])
->via('orderItem');
}

You need to add GROUP BY in your search model to ensure that orders will not be duplicated in query results:
$query->groupBy('order1.id');
Although hasOne() seems to be incorrect (if one order can have multiple items, then it could also have multiple locations), changing this to hasMany() will not fix GridView results. You need to be careful with one-to-many or many-to-many relations, usually you need to use GROUP BY to remove duplicates or adjust your DB structure or joins in search model, to avoid such situation.
BTW: Adding groupBy() in relation definition (getLocation()) is almost always incorrect. This is not a job of relation definition to handle grouping of main model results (you can be almost sure it will create issues with lazy loading).

Related

withExists() or withCount() for nested relationship (Many To Many and One To Many) in Laravel Eloquent

I have the following relationships in models:
Product.php
public function skus()
{
return $this->belongsToMany(Sku::class);
}
Sku.php
public function prices()
{
return $this->hasMany(Price::class);
}
I need to get an attribute indicating whether a product has at least one price or not (in the extreme case, just the number of prices).
Product::withExists('sku.prices') or Product::withCount('sku.prices')
I know about this repository https://github.com/staudenmeir/belongs-to-through, but I prefer to use complex query once
UPDATE: I have already written a sql query for this purpose, but I don't know how to do it in Laravel:
SELECT
*,
EXISTS (SELECT
*
FROM prices
INNER JOIN skus
ON prices.sku_id = skus.id
INNER JOIN product_sku
ON skus.id = product_sku.sku_id
WHERE products.id = product_sku.product_id
) AS prices_exists
FROM products
Here you can get at least one record
$skuPrice = Sku::with('prices')
->has('prices', '>=', 1)
->withCount('prices')
->get();

Yii2 GridView shows "(not set)" if dataProvider contains GROUP BY and INNER JOIN

I have show (only show, not compleate CRUD) the result of a query which is built as following:
SELECT SUM(a) AS ab, b, COUNT(*) as C
FROM x
INNER JOIN y
ON y.a = x.a
WHERE b=123
GROUP BY b
so I built this query with ActiveRecord in SearchModels search() method.
In the model of table a I added a hasOne()-relation.
To display the data of this query, I'm using GridView. In it's columns array I use y.b and so on...
My problem: The columns from table x are displayed correct, but for every "joined column" from table y it displays (not set).If I print the by ActiveRecord builded query, and execute it in my sql client, it displays all data. I guess this is depending on the Models primaryKey() function, but I can't change it to get the table work properly. Does somebody know a solution for my problem or why dataProvider/GridView takes care of the selected model's (in this case model of table x) primaryKey() method (or how to make dataProvider/GridView ignore the primaryKey()?
In model you should create relation method with relation model.
For example:
class Patient extends ActiveRecord
{
public function getOrders()
{
return $this->hasMany(Order::class, ['patient_id' => 'id']);
}
}
class Order extends ActiveRecord
{
public function getPatient()
{
return $this->hasOne(Patient::class, ['id' => 'patient_id']);
}
}
For access data:
// SELECT * FROM `patient` WHERE `id` = 1
$patient = Patient::findOne(1);
$orders = $patient->orders;

How to write query to get specific data from pivot table in laravel?

I'm still a beginner in Laravel. I'm trying to write a query to get categories which are associated with specific place. I have the following three tables
place place_categorye category
------ ---------------- -------------
id place_id id
name category_id name
each place has number of categories
what I want to do is when I choose place_id I get the categories associated with it in the pivot table.
I supposed that you have a Many To Many relation between places and categories then in your Place Model
public function categories(){
return $this->belongsToMany(Category::class, 'place_categorye', 'place_id', 'category_id');
}
And now you can access the categories of a certain Place like below:
$place->categories;
Without the relationship definitions, it is hard to give an answer on the ORM queries, but here is a raw query which will give you the expected result,
DB::select(DB::raw("
select category.id, category.name
from place
join place_categorye on place_categorye.place_id = place.id
join category on category.id = place_categorye.category_id
where place.id = 1");
if you want the results inclusive of all the null values, depending on the use case you can use a left join instead of join (join means innter join by default)
** to many relationship** for this
<?php
namespace App\PlaceCategory;
use Illuminate\Database\Eloquent\Relations\Pivot;
class PlaceCategory extends Pivot {
public function place()
{
return $this->belongsTo('App\Place');
}
public function category()
{
return $this->belongsTo('App\Category');
}
}
category Model
public function places()
{
return $this->hasMany('App\Place')
->using('App\PlaceCategory');
}
Place Model
public function categpries()
{
return $this->hasMany('App\Category')
->using('App\PlaceCategory');
}
Now you can access this easily & can query easily .
Like
$place=Place::with('categories')->first();
by using
$place->categories we get all the categories for this place

Handling relationship in model in laravel

I am learning relationships in Laravel php framework and I am trying to build this query
SELECT * FROM users u INNER JOIN link_to_stores lts ON u.id=lts.user_id INNER JOIN stores s ON lts.store_id=s.store_id WHERE lts.privilege = 'Owner'
I built this in Model
Link_to_store.php
public function store()
{
return $this->belongsTo('App\Store');
}
public function user()
{
return $this->belongsTo('App\User');
}
User.php
public function store_links()
{
return $this->hasMany('App\Link_to_store');
}
Store.php
public function user_links()
{
return $this->hasMany('App\Link_to_store');
}
I tried this query but this only joins user and link_to_store table
$personal_stores = Auth::user()->store_links->where('privilege','=','Owner');
Now I am confused how to join store table too. Can anyone help with this?
Schema is like this
Stores Table
store_id store_name
Users Table
id name
Link_to_stores Table
id store_id user_id privilege
I suppose store_links is actually a pivot table. In this case, you can use belongsToMany(), this will automatically take care of the pivot table.
To do this, in your User model you change the store function to this:
function stores() {
return $this->belongsToMany('App\Store', 'store_links', 'user_id', 'store_id')->withPivot('privilege');
}
Because the primary key of stores is not id, you will have to define this in you Store model with the following line:
protected $primaryKey = 'store_id';
Now to get the stores for a user, you simply call
$stores = Auth::user->stores()->wherePivot('privilege', 'Owner')->get();
I am learning relationships in Laravel php framework and I am trying to build this query
SELECT * FROM users u INNER JOIN link_to_stores lts ON u.id=lts.user_id INNER JOIN stores s ON lts.store_id=s.store_id WHERE lts.privilege = 'Owner'
You are trying to do a join here. You can do a join like this:
$stores = User::join('link_to_stores as lts', 'users.id', '=', 'lts.user_id')->join('stores as s', 'lts.store_id', '=', 's.id')->where('lts.privilege', 'Owner')->get();
But like Jerodev pointed out, it seems like Many to Many relationship might make more sense in your case. The difference is that relationship will actually execute 2 queries (1 for original model, 1 for relationship). It will then attach the related models to the original model (which is extremely handy).

Laravel Eager Load and Group Multiple Joins on Pivot

I have a Pivot table thats used to join two other tables that have many relations per hotel_id. Is there a way I can eagerload the relationship that pulls the results for both tables in one relationship? The raw SQL query, works correctly but when using belongsToMany the order is off.
Amenities Pivot Table
id
hotel_id
distance_id
type_id
Distance Table
id
name
Type Table
id
name
RAW Query (This works fine)
SELECT * FROM amenities a
LEFT JOIN distance d ON a.distance_id = d.id
LEFT JOIN type t ON a.type_id = t.id WHERE a.hotel_id = ?
My "Hotels" Model is using belongsToMany like so
public function distance() {
return $this->belongsToMany('Distance', 'amenities', 'hotel_id', 'distance_id');
}
public function type() {
return $this->belongsToMany('Type', 'amenities', 'hotel_id', 'type_id');
}
This outputs the collection, but they are not grouped correctly. I need to loop these into select fields side by side as entered in the pivot table, so a user can select a "type" and the "distance", but the order is off when using the collection. The raw query above outputs correctly.
Hotels::where('id','=','200')->with('distance', 'type')->take(5)->get();
Ok Solved it. So apparently you can use orderBy on your pivot table. Incase anyone else has this issue this is what I did on both relationships.
public function distance() {
return $this->belongsToMany('Distance', 'amenities', 'hotel_id', 'distance_id')->withPivot('id')->orderBy('pivot_id','desc');
}
public function type() {
return $this->belongsToMany('Type', 'amenities', 'hotel_id', 'type_id')->withPivot('id')->orderBy('pivot_id','desc');
}
It's not really a great practice to include other query building steps in the relationship methods on your models. The relationship method should just define the relationship, nothing else. A cleaner method is to apply eager load constraints. (scroll down a bit) Consider the following.
Hotels::where('id', 200)->with(array(
'distance' => function ($query)
{
$query->withPivot('id')->orderBy('pivot_id','desc');
},
'type' => function ($query)
{
$query->withPivot('id')->orderBy('pivot_id','desc');
},
))->take(5)->get();
If you find that you are eagerly loading this relationship in this way often, consider using scopes to keep things DRY. The end result will allow you to do something like this.
Hotels::where('id', 200)->withOrderedDistance()->withOrderedType()->take(5)->get();
P.S. Your models should be singular. Hotel, not Hotels. The model represents a single record.
Solved by using ->withPivot('id')->orderBy('pivot_id','desc');
Posted answer in the question.

Categories