With spatie/laravel-tags plugin get related items - laravel-5

Using spatie/laravel-tags plugin (https://docs.spatie.be/laravel-tags/v2/basic-usage/using-tags) in my Laravel 5.7 app.
My Vote model (app/Vote.php) has tags:
<?php
namespace App;
use DB;
use App\MyAppModel;
...
use Spatie\Tags\Tag as SpatieTag;
use Spatie\Tags\HasTags;
class Vote extends MyAppModel
{
use HasTags;
protected $table = 'votes';
protected $primaryKey = 'id';
and I try by found Tag get all related Votes, which has this tag, like:
$activeTag = Tag::containingSlug($tag_slug)->first();
$tagRelatedVotes= Vote::withAnyTags( [$activeTag->slug], 'votesTagType' )->get();
But tagRelatedVotes is empty and looking at sql trace I see next:
SELECT *
FROM `tags`
WHERE LOWER(JSON_EXTRACT(slug, "$.en")) like '"%animals%"' limit 1
SELECT *
FROM `tags`
WHERE `name`->'$."en"' = '{"en": "animals"}' AND `type` = 'votesTagType' limit 1
SELECT *
FROM `votes`
WHERE
EXISTS ( SELECT *
FROM `tags`
INNER JOIN `taggables` on `tags`.`id` = `taggables`.`tag_id`
WHERE `votes`.`id` = `taggables`.`taggable_id` AND `taggables`.`taggable_type` = 'App\Vote' AND `id` in (''))
The 1st statement find the row, but the second statement finds nothing and that is strange why name field is used in request ?
So the 3rd statement is invalid. Which is the valid way?
MODIFIED BLOCK # 2:
Thank you for your feedback!
I tried as you wrote, but I got empty results.
I sql trace I see next:
SELECT *
FROM `tags`
WHERE LOWER(JSON_EXTRACT(slug, "$.en")) like '"%thriller%"' limit 1
SELECT *
FROM `tags`
WHERE `name`->'$."en"' = '{"en": "Thriller"}' AND `type` = 'votesTagType' limit 1
SELECT *
FROM `votes`
WHERE
EXISTS ( SELECT *
FROM `tags`
INNER JOIN `taggables` on `tags`.`id` = `taggables`.`tag_id`
WHERE `votes`.`id` = `taggables`.`taggable_id` AND `taggables`.`taggable_type` = 'App\Vote' AND `id` in (''))
The 1st statement returned 1 row, but the second returned nothing, but I have 1 row with name= 'Thriller' and type = 'votesTagType'.
I am not sure what expression
`name`->'$."en"' = '{"en": "Thriller"}'
means, that is beyond my mysql expierence. CXan it be some mysql or this plugin options?
I have :
SELECT version() : 5.7.23-0ubuntu0.18.04.1
Thanks!

You have to pass the name of the Tag to the withAnyTags() method. docs
$activeTag = Tag::containingSlug($tag_slug)->first();
$tagRelatedVotes= Vote::withAnyTags( [$activeTag], 'votesTagType' )->get();

Related

Which is the best efficient way to get many-to-many relation count in laravel?

I have students and subjects table in many-to-many relation (pivot table is student_subject).
Student Model
public function subjects()
{
return $this->belongsToMany(Subject::class, 'student_subject');
}
Subject Model
public function students()
{
return $this->belongsToMany(Student::class, 'student_subject');
}
Here I want the particular student subjects counts. I tried the below methods it's working fine but I want the best efficient way for this purpose.
1.
$student = Student::find($id);
$subject_count = $student->subjects()->count();
I checked the SQL query through laravel debuger it shows as below
select * from `students` where `students`.`id` = '10' limit 1
select count(*) as aggregate from `subjects` inner join `student_subject` on `subjects`.`id` = `student_subject`.`subject_id` where `student_subject`.`student_id` = 10 and `subjects`.`deleted_at` is null
$student = Student::withCount('subjects')->find($id);
$subject_count = $student->subjects_count;
I checked the SQL query through laravel debuger it shows as below
select `students`.*, (select count(*) from `subjects` inner join `student_subject` on `subjects`.`id` = `student_subject`.`subject_id` where `students`.`id` = `student_subject`.`student_id` and `subjects`.`deleted_at` is null) as `subjects_count` from `students` where `students`.`id` = '10' limit 1
$student = Student::find($id);
$subject_count = $student->loadCount('subjects')->subjects_count;
I checked the SQL query through laravel debuger it shows as below
select * from `students` where `students`.`id` = '10' limit 1
select `id`, (select count(*) from `subjects` inner join `student_subject` on `subjects`.`id` = `student_subject`.`subject_id` where `students`.`id` = `student_subject`.`student_id` and `subjects`.`deleted_at` is null) as `subjects_count` from `students` where `students`.`id` in (10)
$student = Student::find($id);
$subject_count = DB::table('student_subject')->where('student_id', $student->id)->count();
I checked the SQL query through laravel debuger it shows as below
select * from `students` where `students`.`id` = '10' limit 1
select count(*) as aggregate from `student_subject` where `student_id` = 10
According to the above ways which one is best and why? or if any different best way also there?
Doing relation()->count() is probably faster.
But if all you need is the count, withCount() should be better in terms of memory consumption.

Laravel Eloquent Query With OR WHERE Condition

I am using Laravel 6 & mysql 7
I have below query
$tasks = Task::where('deleted_at', null)->where('company_id',$company_id);
$tasks = $tasks->where('created_by',$user_id);
$tasks = $tasks->orWhereIn('id',$task_ids);
It generates below raw query when i print it
SELECT * FROM `tasks` WHERE `deleted_at` IS NULL AND `company_id` = 25 AND `created_by` = 20 OR
`id` IN(112,...215) ORDER BY `id` DESC
Now Id 112 is deleted but still showing in result, although i have where('deleted_at', null) condition but it is not working
I want to apply all other conditions on $task_ids as well
How this can be achieved with optimized query?
UPDATED: Here is complete scenario
I want to select all records which is created by me or assigned to me. Here is my complete code.
$tasks = Task::where('deleted_at', null)->where('company_id',$company_id);
$tasks = $tasks->where('created_by',$user_id);
$task_ids = TaskUser::where('user_id',$user_id)->pluck('task_id')->all();
$tasks = $tasks->orWhereIn('id',$task_ids);
This is because the AND operator has a higher precedence than OR, which basically means that AND "sticks" together more than OR does. You query basically is interpredeted like this:
SELECT * FROM `tasks`
WHERE
(`deleted_at` IS NULL AND `company_id` = 25 AND `created_by` = 20)
OR
( `id` IN(112,...215) )
I am not entirly sure wheter you actually want to OR anything. If you really want to apply all conditions, you just need to change the orWhereIn to a whereIn.
In case you want all not-deleted tasks, that EITHER belong to a company and a auser OR whose id is in the list, you would need to update your query like this:
$tasks = Task::where('deleted_at', null);
$tasks = $tasks->where(function($q) use ($user_id, $task_ids){
$q->where(function($q2) use ($user_id, $task_ids) {
$q2->where('created_by',$user_id)
->where('company_id',$company_id);
})
->orWhereIn('id',$task_ids);
});
which should result in this query:
SELECT * FROM `tasks`
WHERE `deleted_at` IS NULL AND (
( `company_id` = 25 AND `created_by` = 20 )
OR
`id` IN(112,...215)
)
There is actually a chapter about parameter grouping in the excellent laravel documentation as well.

How to use SQL raw query in Laravel

SQL table:
SELECT id,
account_name,
parent_id
FROM
(SELECT id,
account_name,
parent_id,
CASE
WHEN id = 1 THEN #idlist := CONCAT(id)
WHEN FIND_IN_SET(parent_id, #idlist) THEN #idlist := CONCAT(#idlist, ',', id)
END AS checkId
FROM chart_of_account
ORDER BY id ASC) AS T
WHERE checkId IS NOT NULL
When I run this query in MySQL it works fine and the result is fetched perfectly, but when I run it in Laravel like this:
$accountId = DB::select('SELECT id,account_name,parent_id FROM
(SELECT id,account_name,parent_id,
CASE WHEN id = '.$account_id.' THEN #idlist := CONCAT(id)
WHEN FIND_IN_SET(parent_id,#idlist) THEN #idlist := CONCAT(#idlist,', ',id)
END as checkId
FROM chart_of_account
ORDER BY id ASC) as T
WHERE checkId IS NOT NULL');
it gives an error.
Argument 1 passed to Illuminate\\Database\\Connection::prepareBindings() must be of the type array, string given,
Try this:
$query = 'YOUR_QUERY_THE_BIG_ONE:)';
$result = DB::select($query,[]);
dd($result);
Optionally, you can use ? sign in your query wherever you are using user inputs to prevent mySQL injection issue and then provide their value in second parameter array of select function. One example would be:
$inputUserEmail = $request->input('email');
$query = 'SELECT * FROM users WHERE email=?';
$result = DB::select($query,[$inputUserEmail]);
dd($result);
I hope it gives you an idea

mysqli_num_rows() expects parameter 1 to be mysqli_result, boolean given in on line 285

$get_list_pro = "select * from products where product_list='$list_id'";
$run_list_pro = mysqli_query($con, $get_list_pro);
$count_lists = mysqli_num_rows($run_list_pro);*
That error occurs when your select query is wrong. The item that you are selecting using select * from products where product_list='$list_id. Does not exists in table product.

Laravel: Improved pivot query

I am successfully querying following and it create 130 queries, I want to optimise it and reduce the number of queries, I have set upped the model and controllers following way.
Post Modal
class Post extends Eloquent {
public function Categories () {
return $this->belongsToMany('Category', 'category_post');
}
}
Category Modal
class Category extends Eloquent {
public function posts () {
return $this->belongsToMany('Post', 'category_post');
}
}
and in the Controller, I am using following query, what following query does is, querying the results based on category id.
$category = Category::with('posts')->where('id','=',$id)->paginate(10)->first();
return Response::json(array('category' => $category));
If anyone can give me a hand to optimise the query, would be really greatful.
You are wrong, it doesn't create 130 queries.
It will create the following 3 queries:
select count(*) as aggregate from `categories` where `id` = '5';
select * from `categories` where `id` = '5' limit 10 offset 0;
select `posts`.*, `posts_categories`.`category_id` as `pivot_category_id`, `posts_categories`.`post_id` as `pivot_post_id` from `posts` inner join `posts_categories` on `posts`.`id` = `posts_categories`.`post_id` where `posts_categories`.`category_id` in ('5');
But the question is what exactly you want to paginate. Now you paginate categories and it doesn't make much sense because there's only one category with selected $id.
What you probably want to get is:
$category = Category::where('id','=',$id)->first();
$posts = $category->posts()->paginate(10);
and this will again create 3 queries:
select * from `categories` where `id` = '5' limit 1;
select count(*) as aggregate from `posts` inner join `posts_categories` on `posts`.`id` = `posts_categories`.`post_id` where `posts_categories`.`category_id` = '5';
select `posts`.*, `posts_categories`.`category_id` as `pivot_category_id`, `posts_categories`.`post_id` as `pivot_post_id` from `posts` inner join `posts_categories` on `posts`.`id` = `posts_categories`.`post_id` where `posts_categories`.`category_id` = '5' limit 10 offset 0;
If you would like to improve it, you will probably need to not use Eloquent in this case and use join - but is it worth it? You would now need to manually paginate results without paginate() so it would probably won't be want you want to achieve.
EDIT
What you probably do is:
you get all posts that belongs to the category (but in fact you want to paginate only 10 of them)
for each post you want do display all categories it belongs to.
To lower number of queries you should use:
$category = Category::where('id','=',$id)->first();
$posts = $category->posts()->with('categories')->paginate(10);
and to display it you should use:
foreach ($posts as $p) {
echo $p->name.' '."<br />";
foreach ($p->categories as $c) {
echo $c->name."<br />";
}
}
It should lower your number queries to 4 from 130

Resources