WhereNotExists Laravel Eloquent - php

Little bit of trouble with the eloquent framework for laravel.
I need to replicate a query like this :
SELECT *
FROM RepairJob
WHERE NOT EXISTS (SELECT repair_job_id
FROM DismissedRequest
WHERE RepairJob.id = DismissedRequest.repair_job_id);
Right now I have
$repairJobs = RepairJob::with('repairJobPhoto', 'city', 'vehicle')->where('active', '=', 'Y')->whereNotExists('id', [DismissedRequest::all('repair_job_id')])->get();
Anyone an idea? I need to get all the repairjobs where there is no record for in the dismissed requests table
I get this error when using the query above
Argument 1 passed to Illuminate\Database\Query\Builder::whereNotExists() must be an instance of Closure, string given

Try this:
$repairJobs = RepairJob::with('repairJobPhoto', 'city', 'vehicle')
->where('active', '=', 'Y')
->whereNotExists(function($query)
{
$query->select(DB::raw(1))
->from('DismissedRequest')
->whereRaw('RepairJob.id = DismissedRequest.id');
})->get();

Try doesntHave() method. Assuming 'dismissedRequests' as relation name in RepairJob model.
$jobs = RepairJob::with('repairJobPhoto', 'city', 'vehicle')
->where('active', 'Y')->doesntHave('dismissedRequests')->get();

Related

Need help for my sql query to make use of AS operator [duplicate]

Lets say we are using Laravel's query builder:
$users = DB::table('really_long_table_name')
->select('really_long_table_name.id')
->get();
I'm looking for an equivalent to this SQL:
really_long_table_name AS short_name
This would be especially helpful when I have to type a lot of selects and wheres (or typically I include the alias in the column alias of the select as well, and it gets used in the result array). Without any table aliases there is a lot more typing for me and everything becomes a lot less readable. Can't find the answer in the laravel docs, any ideas?
Laravel supports aliases on tables and columns with AS. Try
$users = DB::table('really_long_table_name AS t')
->select('t.id AS uid')
->get();
Let's see it in action with an awesome tinker tool
$ php artisan tinker
[1] > Schema::create('really_long_table_name', function($table) {$table->increments('id');});
// NULL
[2] > DB::table('really_long_table_name')->insert(['id' => null]);
// true
[3] > DB::table('really_long_table_name AS t')->select('t.id AS uid')->get();
// array(
// 0 => object(stdClass)(
// 'uid' => '1'
// )
// )
To use aliases on eloquent models modify your code like this:
Item
::from( 'items as items_alias' )
->join( 'attachments as att', DB::raw( 'att.item_id' ), '=', DB::raw( 'items_alias.id' ) )
->select( DB::raw( 'items_alias.*' ) )
->get();
This will automatically add table prefix to table names and returns an instance of Items model. not a bare query result.
Adding DB::raw prevents laravel from adding table prefixes to aliases.
Here is how one can do it. I will give an example with joining so that it becomes super clear to someone.
$products = DB::table('products AS pr')
->leftJoin('product_families AS pf', 'pf.id', '=', 'pr.product_family_id')
->select('pr.id as id', 'pf.name as product_family_name', 'pf.id as product_family_id')
->orderBy('pr.id', 'desc')
->get();
Hope this helps.
To use in Eloquent.
Add on top of your model
protected $table = 'table_name as alias'
//table_name should be exact as in your database
..then use in your query like
ModelName::query()->select(alias.id, alias.name)
You can use less code, writing this:
$users = DB::table('really_long_table_name')
->get(array('really_long_table_name.field_very_long_name as short_name'));
And of course if you want to select more fields, just write a "," and add more:
$users = DB::table('really_long_table_name')
->get(array('really_long_table_name.field_very_long_name as short_name', 'really_long_table_name.another_field as other', 'and_another'));
This is very practical when you use a joins complex query
I have tried all these options and none works for me. Then I had found something in the Laravel documentation that really works.
You could try this:
DB::table('table_one as t1')
->select(
't1.field_id as id','t2.field_on_t2 as field'
)->join('table_two as t2', function ($join) {
$join->on('t1.field_id ', '=', 't2.field_id');
})->get()
Also note that you can pass an alias as the second parameter of the table method when using the DB facade:
$users = DB::table('really_long_table_name', 'short_name')
->select('short_name.id')
->get();
Not sure if this feature came with a specific version of Laravel or if it has always been baked in.
Same as AMIB answer, for soft delete error "Unknown column 'table_alias.deleted_at'",
just add ->withTrashed() then handle it yourself like ->whereRaw('items_alias.deleted_at IS NULL')
In the latest version of Laravel 9, you can use alias name for column as:
$events = Booking::whereBetween('sessionDateTime', [$today, $nextMonth])->get(['bookings.sessionDateTime as start']); // start is an alias here

Laravel OrderBy Nested Collection

I'm using a Roles package (similar to entrust). I'm trying to sort my User::all() query on roles.id or roles.name
The following is all working
User::with('roles');
This returns a Collection, with a Roles relation that also is a collection.. Like this:
I'm trying to get all users, but ordered by their role ID.
I tried the following without success
maybe because 'roles' returns a collection? And not the first role?
return App\User::with(['roles' => function($query) {
$query->orderBy('roles.id', 'asc');
}])->get();
And this
return App\User::with('roles')->orderBy('roles.id','DESC')->get();
None of them are working. I'm stuck! Can someone point me in the right direction please?
You can take the help of joins like this:
App\User::join('roles', 'users.role_id', '=', 'roles.id')
->orderBy('roles.id', 'desc')
->get();
Hope this helps!
You can make accessor which contains role id or name that you want to sort by.
Assume that the accessor name is roleCode. Then App\User::all()->sortBy('roleCode') will work.
Here's the dirty trick using collections. There might be a better way to achieve this(using Paginator class, I guess). This solution is definitely a disaster for huge tables.
$roles = Role::with('users')->orderBy('id', 'DESC')->get();
$sortedByRoleId = collect();
$roles->each(function ($role) use($sorted) {
$sortedByRoleId->push($role->users);
});
$sortedByRoleId = $sortedByRoleId->flatten()->keyBy('id');
You can sort your relations by using the query builder:
notice the difference with your own example: I don't set roles.id but just id
$users = App\User::with(['roles' => function ($query) {
$query->orderBy('id', 'desc');
}])->get();
See the Official Laravel Docs on Constraining Eager Loading
f you want to order the result based on nested relation column, you must use a chain of joins:
$values = User::query()->leftJoin('model_has_roles', function ($join)
{
$join>on('model_has_roles.model_id', '=', 'users.id')
->where('model_has_roles.model_type', '=', 'app\Models\User');})
->leftJoin('roles', 'roles.id', '=', 'model_has_roles.role_id')
->orderBy('roles.id')->get();
please note that if you want to order by multiple columns you could add 'orderBy' clause as much as you want:
->orderBy('roles.name', 'DESC')->orderby('teams.roles', 'ASC') //... ext
check my answer here:
https://stackoverflow.com/a/61194625/10573560

laravel convert to string mysql

I have this code written in using the laravel and I wonder how to write it in pure SQL without using laravel, as would be?
Route::get('popular', function(){
$media = Media::where('active', '=', '1')->join('media_likes', 'media.id', '=', 'media_likes.media_id')->groupBy('media_likes.media_id')->orderBy(DB::raw('COUNT(media_likes.id)'), 'DESC')->select('media.*')->paginate(30);
$data = array(
'media' => $media,
'categories' => Category::all(),
'pages' => Page::all(),
'settings' => Setting::first(),
);
return View::make('Theme::home', $data);
});
Using toSql method
Laravel Query builder has a helpful method called toSql which is handy to see what the final SQL query looks like.
Now, when you call the paginate method, you'll get a Paginator and you won't be able to call the toSql method.
Deconstructing paginate
When you use paginate, Laravel will make the following query for each $page:
// Let's simplify the query for now
Media::skip(($page - 1) * $perPage)->take($perPage);
Knowing that, you can use the query builder's method toSql and you'll see the actual sql string.
Supossing $page=3, $perPage=30, and that the name of the table is media, you'll get something like this:
Media::skip(2 * 30)->take(30)->toSql();
select * from media limit 30 offset 60
Now, for your actual query, you can use the following to see the resulting SQL string for the page 3 (as an example):
Media::where('active', '=', '1')
->join('media_likes', 'media.id', '=', 'media_likes.media_id')
->groupBy('media_likes.media_id')
->orderBy(DB::raw('COUNT(media_likes.id)'), 'DESC')
->select('media.*')
->skip(2 * 30)
->take(30)
->toSql();
Listening For Query Events
Alternatively, you can set an event listener in your AppServiceProvider, and log each SQL query the application executes.
public function boot()
{
\DB::listen(function ($query) {
\Log::debug($query->sql);
\Log::debug($query->bindings);
\Log::debug($query->time);
});
}
$media = Media::where('active', '=', '1')->join('media_likes', 'media.id', '=', 'media_likes.media_id')->groupBy('media_likes.media_id')->orderBy(DB::raw('COUNT(media_likes.id)'), 'DESC')->select('media.*')->paginate(30);
Something like this:
SELECT m.*
FROM
media m
JOIN
media_likes ml
ON
ml.media_id = m.id
GROUP BY
ml.media_id
ORDER BY
COUNT(ml.id) DESC

Laravel using UNION in query builder

I have an SQL query that works fine and I'm trying to convert into fluent::
SELECT DISTINCT tags.tag
FROM tags, items
WHERE tags.taggable_type = 'Item'
AND items.item_list_id = '1'
UNION
SELECT DISTINCT tags.tag
FROM tags, itemlists
WHERE tags.taggable_type = 'ItemList'
AND itemlists.id = '1'
This is what I have so far in fluent, it all seems right as far as I can tell from the docs and the individual queries both work on their own, it's just when I UNION them it throws an error:
$itemTags = Tag::join('items', 'items.id', '=', 'tags.taggable_id')
->select('tags.tag')
->distinct()
->where('tags.taggable_type', '=', 'Item')
->where('items.item_list_id', '=', $itemList->id);
$itemListTags = Tag::join('itemlists', 'itemlists.id', '=', 'tags.taggable_id')
->select('tags.tag')
->distinct()
->where('tags.taggable_type', '=', 'ItemList')
->where('itemlists.id', '=', $itemList->id);
// the var_dump below shows the expected results for the individual queries
// var_dump($itemTags->lists('tag'), $itemListTags->lists('tag')); exit;
return $itemTags
->union($itemListTags)
->get();
I get the following error when I run it (I've also swapped from Ardent back to Eloquent on the model in case that made a difference - it doesn't):
Argument 1 passed to Illuminate\Database\Query\Builder::mergeBindings() must be an instance of Illuminate\Database\Query\Builder, instance of LaravelBook\Ardent\Builder given, called in path/to/root\vendor\laravel\framework\src\Illuminate\Database\Query\Builder.php on line 898 and defined
Looks like your models are using Ardent, not Eloquent:
...instance of LaravelBook\Ardent\Builder given, ...
And probably this might be a problem on Ardent, not Laravel.
Open an issue here: https://github.com/laravelbook/ardent.
EDIT:
Try to change use QueryBuilder instead of Eloquent:
Use this for QueryBuilder:
DB::table('tags')->
Instead of the Eloquent way:
Tag::
I know you mentioned wanting to use the query builder, but for complex queries that the builder might throw fits on, you can directly access the PDO object:
$pdo = DB::connection()->getPdo();

How to alias a table in Laravel Eloquent queries (or using Query Builder)?

Lets say we are using Laravel's query builder:
$users = DB::table('really_long_table_name')
->select('really_long_table_name.id')
->get();
I'm looking for an equivalent to this SQL:
really_long_table_name AS short_name
This would be especially helpful when I have to type a lot of selects and wheres (or typically I include the alias in the column alias of the select as well, and it gets used in the result array). Without any table aliases there is a lot more typing for me and everything becomes a lot less readable. Can't find the answer in the laravel docs, any ideas?
Laravel supports aliases on tables and columns with AS. Try
$users = DB::table('really_long_table_name AS t')
->select('t.id AS uid')
->get();
Let's see it in action with an awesome tinker tool
$ php artisan tinker
[1] > Schema::create('really_long_table_name', function($table) {$table->increments('id');});
// NULL
[2] > DB::table('really_long_table_name')->insert(['id' => null]);
// true
[3] > DB::table('really_long_table_name AS t')->select('t.id AS uid')->get();
// array(
// 0 => object(stdClass)(
// 'uid' => '1'
// )
// )
To use aliases on eloquent models modify your code like this:
Item
::from( 'items as items_alias' )
->join( 'attachments as att', DB::raw( 'att.item_id' ), '=', DB::raw( 'items_alias.id' ) )
->select( DB::raw( 'items_alias.*' ) )
->get();
This will automatically add table prefix to table names and returns an instance of Items model. not a bare query result.
Adding DB::raw prevents laravel from adding table prefixes to aliases.
Here is how one can do it. I will give an example with joining so that it becomes super clear to someone.
$products = DB::table('products AS pr')
->leftJoin('product_families AS pf', 'pf.id', '=', 'pr.product_family_id')
->select('pr.id as id', 'pf.name as product_family_name', 'pf.id as product_family_id')
->orderBy('pr.id', 'desc')
->get();
Hope this helps.
To use in Eloquent.
Add on top of your model
protected $table = 'table_name as alias'
//table_name should be exact as in your database
..then use in your query like
ModelName::query()->select(alias.id, alias.name)
You can use less code, writing this:
$users = DB::table('really_long_table_name')
->get(array('really_long_table_name.field_very_long_name as short_name'));
And of course if you want to select more fields, just write a "," and add more:
$users = DB::table('really_long_table_name')
->get(array('really_long_table_name.field_very_long_name as short_name', 'really_long_table_name.another_field as other', 'and_another'));
This is very practical when you use a joins complex query
I have tried all these options and none works for me. Then I had found something in the Laravel documentation that really works.
You could try this:
DB::table('table_one as t1')
->select(
't1.field_id as id','t2.field_on_t2 as field'
)->join('table_two as t2', function ($join) {
$join->on('t1.field_id ', '=', 't2.field_id');
})->get()
Also note that you can pass an alias as the second parameter of the table method when using the DB facade:
$users = DB::table('really_long_table_name', 'short_name')
->select('short_name.id')
->get();
Not sure if this feature came with a specific version of Laravel or if it has always been baked in.
Same as AMIB answer, for soft delete error "Unknown column 'table_alias.deleted_at'",
just add ->withTrashed() then handle it yourself like ->whereRaw('items_alias.deleted_at IS NULL')
In the latest version of Laravel 9, you can use alias name for column as:
$events = Booking::whereBetween('sessionDateTime', [$today, $nextMonth])->get(['bookings.sessionDateTime as start']); // start is an alias here

Categories