Sorting by joined data Yii2 - php

In my Yii2 project I have post and post_views tables, and a Post model.
There are 2 fields in post_views:
post_id
views_counter
I'm using PostSearch and PostsQuery (ActiveQuery) for my queries.
My Task is: I need to get all my posts with custom field views where I get views_counter from post_views.
I'm not using hasMany in model because there is no model for post_views table in the project and I'd prefer not to create it if possible. Also, I need to sort my posts by views field. I'm stuck on this:
public function topPosts(){
$junction_table = '{{%post_views}}';
return $this->innerJoin($junction_table, Post::tableName().'.id='.$junction_table.'.post_id');
}
The main problem is that I don't know how to join and return data properly.
I need this query:
SELECT p.*, pv.views_count FROM posts p INNER JOIN post_views pv ON p.id = pv.post_id ORDER BY pv.views_count DESC;

First, you need to update your Post model with viewCount field:
class Post extends \yii\db\ActiveRecord
{
private $viewCount;
public static function tableName()
{
return "posts";
}
public function setViewCount($viewCount)
{
$this->viewCount = $viewCount;
}
public function getViewCount()
{
return $this->viewCount;
}
}
Then you need to include viewCount field in select list like this:
$post = new Post();
$query = $post->find()
->alias('p')
->select(['p.*', 'pv.views_count viewCount'])
->innerJoin("post_views pv", "p.Id = pv.id")
->limit(100)
->orderBy(["pv.views_count" => SORT_DESC]);
//Get SQL query string
echo $query->createCommand()->getSql();
//Execute query
$result = $query->all();

Related

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

Issue with group by laravel and sql

Firstly I have problem which count products which are sold every day. In sql I have query
select product_name, sum(quantity) as quantity from invoice_product
join invoices on invoices.id = invoice_product.invoice_id
join products on products.id = invoice_product.product_id
where invoices.issued_at = '2019-05-16'
and products.`made_by_us` = 1
group by product_name
It show me interesting for me information but I used product_name to make group by but I should use product_id - I need show name too but I don't know how to do it.
Secondly I want to use it in Laravel so maybe someone know which is it possible to do it in Eloquent?
Thank you in advance :)
I would go with withCount() combined with select(DB::raw()), like this:
$products = Product::withCount(['invoices as quantity' => function ($query) {
$query->select(DB::raw('sum(quantity)'));
}])->get();
Then, you can access each quantity sum like this:
$quantity = $products->first()->quantity;
You would need to update your model relationships to achieve that.
Models:
InvoiceProduct Model
class InvoiceProduct extends Model
{
protected $table = 'invoice_product';
protected $guarded = [
'id',
];
}
public function invoice()
{
return $this->belongsTo('App\Invoice'); // Assuming `Invoice` Model is directly in app folder
}
public function product()
{
return $this->belongsTo('App\Product'); // Assuming `Product` Model is directly in app folder
}
Controller:
$full_query = InvoiceProduct::whereHas('invoice', function ($query) {
return $query->where('issued_at', '2019-05-16');
})->whereHas('product', function ($query) {
return $query->where('made_by_us', 1);
});
$product_names = $full_query->get(['product_name']);
$total_quantities = $full_query->sum('quantity');

Selecting data from different tables in laravel

I have a table of articles defined by their ID, name, price and category_ID and a table of categories defined by category_ID and name.
I want to select into my controller, the list of articles, along with the category name.
How to do that?
My answer assumes you keep the models in App\Models folder
In your Articles model define the following method.
public function category()
{
return $this->belongsTo('App\Models\Category');
}
You can access it now via $myArticle->category->name;
Make sure in Categories model the correct table is defined, based on your question i can not make up the categorie table.
Put $table = 'categories'; in the category model or whatever the table name is.
with raw SQL
$query = "SELECT articles.* , categories.name AS categoryName FROM articles JOIN categories ON articles.category_ID = categories. category_ID"
$result = \DB::select(SQL);
dump($result)
or
with Eloquent you can add a method to your model to return relationship , let's call it category
class Article extends Model {
public function category(){
return $this->hasOne("App/Category" , "category_ID" , "category_ID");
}
}
now you can do this
$article = Article::find(1);
dump($article->category->name);
checkout hasOne method from the docs

i get this error: Object of class Illuminate\Database\Eloquent\Builder could not be converted to string in laravel ORM

I get this error:
Object of class Illuminate\Database\Eloquent\Builder could not be converted to string
when I run this code:
public function index()
{
save_resource_url();
//$items = News::with(['category', 'photos'])->get();
$items = Solicitud::rightjoin(News::with(['category', 'photos']),'news.id','=','solicitud.event_id')->count('*','event_id','as','total')->get();
return $this->view('news_events.index', compact('items'));
}
my original sql query
SELECT *,count(event_id) as total FROM solicitud RIGHT JOIN news ON news.id = solicitud.event_id group by title;
The error you are getting is because you are putting the Builder as first parameter News::with(['category', 'photos']). it should only be the string(table name) like 'news'.
Click here to read more
So the query should
$items = Solicitud::rightjoin( 'news','news.id','=','solicitud.event_id')->count('*','event_id','as','total')->get();
Solve:
my original code
public function index()
{
save_resource_url();
$items = News::with(['category', 'photos'])->get();
return $this->view('news_events.index', compact('items'));
}
change my sql query:
SELECT *,count(event_id) as total FROM solicitud RIGHT JOIN news ON news.id = solicitud.event_id group by title;
this query produced duplicate columns
for this:
select news.*,count(event_id) as total from news left join solicitud on solicitud.event_id = news.id group by news.id;
this query shows only the columns of the users table plus the 'total' table in relation to the 'request' table
in my code transform to eloquent
public function index()
{
save_resource_url();
$items = News::with(['category', 'photos'])->leftjoin('solicitud','solicitud.event_id','=','news.id')->groupBy('news.id')->select('news.*',DB::raw('count(event_id) as total'))->get();
return $this->view('news_events.index', compact('items'));
}

Laravel - sync with extra column

I have 3 tables
type
type_id
person
person_id
category
category_id
table_name
table_id
person_id
In category I have connections of different tables/models with Type model, So if I have want to get type_id connected with person with person_id = 23 the query should look like this:
SELECT * FROM category WHERE table_name='person' AND table_id = 23
In my Person model I defined relationship with Type this way:
public function groups()
{
return $this->belongsToMany('Type', 'category',
'table_id', 'type_id')->wherePivot( 'table_name', '=', 'person' );
}
When I want to get those types and I use:
$person->groups()->get()
The query looks like this:
select `type`.*, `category`.`table_id` as `pivot_table_id`, `category`.`type_id` as `pivot_type_id` from `type` inner join `category` on `type`.`type_id` = `category`.`type_id` where `category`.`table_id` = '23' and `category`.`table_name` = 'person';
so it seems to be correct.
But I would like to use sync() for synchronizing types with persons and here's the problem.
When I use:
$person->groups()->sync('1' => ['table_name' => 'person']);
I see the query that gets all records from category to use for sync looks like this:
select `type_id` from `category` where `table_id` = '23';
so it doesn't use
`category`.`table_name` = 'person'
condition so synchronization won't work as expected.
Is there any simple way to solve it or should I synchronize it manually?
You should use Eloquents polymorphic relations (http://laravel.com/docs/4.2/eloquent#relationships)
class Category extends Eloquent {
public function categorizable()
{
return $this->morphTo();
}
}
class Person extends Eloquent {
public function categories()
{
return $this->morphMany('Category', 'categorizable');
}
}
Now we can retrieve catgories from person:
$person = Person::find(1);
foreach ($person->categories as $category)
{
//
}
and access person or other owner from category:
$category = Category::find(1);
$categorizable_model = $category->categorizable; //e.g. Person
I can confirm that it was a bug in Laravel 5 commit I used. I've upgraded for Laravel 5 final version and now query is generated as it should.

Categories