How to write this query in laravel? - laravel-5

I want to take maximum 3 notification of specific user that are unread and also their create_at date is lower than or equal to specific date, i want something like bellow query:
select count(*) as aggregate from
(
select * from `notifications`
where `notifications`.`user_id` = '20' and `notifications`.`user_id` is not null and `is_read` = '0'
and created_at <= '2015-07-12 11:41:10' limit 3
) AS src
just pleas consider bellow query:
$displayedNotifications = $user->notifications(function($query) use ($timestamps) {
return $query->Where('created_at', '<=', $timestamps);
})->unread()->take(3)->count();

Hi #jones you have to add a scope method scopeUnread to Notification model as :
function scopeUnread($query, $timestamp) {
$query->where('is_read', false);
if (! empty($timestamp) )
$query->where('created_at', '<=', $timestamp);
return $query;
}
now you can access that scope from User model like below:
$user->notifications()->unread($timestamp);
and
$user->notifications()->unread($timestamp)->take(3)->count();
To have unread notifications count.
Hope it works for you

Related

Query a table with info from another one in Eloquent

I have two tables: Invoices and Clients. Each invoice have a client and each client has an expiration time for their invoices.
I want to get each invoice where the invoice date + the expiration time has passed current date.
I actualy did it with DB::select like this.
DB::select('SELECT DISTINCT i.id,(i.invoice_date+INTERVAL \'1 day\' * invoice_expiration) as expiration_date, i.invoice_date, c.invoice_expiration, c.id as client_id, c.name, i.total, i.status, c.rut FROM invoices i, clients c, invoice_cargos ic, cargos ca WHERE i.invoice_date+INTERVAL \'1 day\' * invoice_expiration < current_date AND i.client_id = c.id AND i.status not in (\'PAID\') and i.id = ic.invoice_id and ic.cargo_id = ca."id" AND i.iva = true AND ca.stage = \'APPROVED\' order by c.name, i.invoice_date ;');
But I'm having trouble translating that to eloquent.
The Invoice model has the relationship to the Client one:
Invoice Model:
public function client()
{
return $this->hasOne(Client::class,'id', 'client_id');
}
Laravel tries to guess, by conventions, the names of the columns in the relationships. I don't think id should be there as the second parameter. Also, you can omit client_id it would be easy for Laravel to guess that relationship, so, I'd leave it like this:
public function client()
{
return $this->hasOne(Client::class);
}
Something like this should work:
Invoice::select(DB::raw('DISTINCT i.id, (i.invoice_date + INTERVAL "1 day" * invoice_expiration) as expiration_date, i.invoice_date, c.invoice_expiration, c.id as client_id, c.name, i.total, i.status, c.rut'))
->join('clients as c', 'i.client_id', '=', 'c.id')
->join('invoice_cargos as ic', 'i.id', '=', 'ic.invoice_id')
->join('cargos as ca', 'ic.cargo_id', '=', 'ca.id')
->whereRaw('i.invoice_date + INTERVAL "1 day" * invoice_expiration < current_date')
->whereNotIn('i.status', ['PAID'])
->where('i.iva', true)
->where('ca.stage', 'APPROVED')
->orderBy('c.name')
->orderBy('i.invoice_date')
->get();
Alternatively, you can create columns in the database to store the calculated values so that you don't have to use raw SQL in your Eloquent query. For example, you could add a expiration_date column to the invoices table and update it with the calculated value when creating or updating an invoice. This would allow you to use a simple Eloquent query like this:
Invoice::where('expiration_date', '<', Carbon::now())
->whereNotIn('status', ['PAID'])
->where('iva', true)
->join('invoice_cargos as ic', 'invoices.id', '=', 'ic.invoice_id')
->join('cargos as ca', 'ic.cargo_id', '=', 'ca.id')
->where('ca.stage', 'APPROVED')
->orderBy('clients.name')
->orderBy('invoices.invoice_date')
->get();

Eloquent whereHas Relationship with constraint on particular related Entity

Consider having two models User, and Book the last one has a status column that can obtain different string values active, inactive, deleted, so the user can have multiple books and the book belongs to the user.
how could I get only users that have their last book status = 'inactive'?
The SQL Query for the behavior is given below:
SELECT
*
FROM
`users`
WHERE EXISTS
(
SELECT
*
FROM
`books`
WHERE
`books`.`user_id` = `users`.`id` AND `books`.`status` = 'inactive' AND `books`.`id` =(
SELECT
nested.`id`
FROM
`books` AS nested
WHERE
nested.`user_id` = `users`.`id`
ORDER BY
nested.`created_at` DESC
LIMIT 1
)
)
I'm using Laravel 5.6
Create additional relationship in User model that returns wanted result. Basically you need 1-1 relationship for this.
/**
* #return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function inactiveBookStillLatestPerUser()
{
return $this->hasOne(Book::class)->where(['status' => 'inactive', 'id' => function (\Illuminate\Database\Query\Builder $nested) {
$nested->from('books as nested')
->selectRaw('max(id)')
->whereRaw('nested.user_id = books.user_id');
}]);
}
Then in somewhere in code (i.e. controller) you call it with
$users = User::has('inactiveBookStillLatestPerUser')->get();
// or if books are needed too
// $users = User::has('inactiveBookStillLatestPerUser')->with(['inactiveBookStillLatestPerUser'])->get();
I used id latest order [max(id)] in subquery to avoid unwanted result if one user made multiple books batch insert at same point of time and when all those books would have same time of insert so latest per created_at wouldn't be most accurate, maybe. But you can do that similarly, instead:
/**
* #return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function inactiveBookStillLatestPerUser()
{
return $this->hasOne(Book::class)->where(['status' => 'inactive', 'created_at' => function (\Illuminate\Database\Query\Builder $nested) {
$nested->from('books as nested')
->selectRaw('max(created_at)')
->whereRaw('nested.user_id = books.user_id');
}]);
}
Maybe second example is ok, but first example with id would work fine, though.
User::where('your-conditions')
->whereHas('books', function($query) {
$query->where('books.status', '=', 'inactive')
->orderBy('id', 'desc')
->first();
})->get();

How to get data based in comparing value in pivote table with value in required table in laravel 5.6?

I am learning larvel 5.6 so I am trying to retrieve number of messages that have id larger than last_seen_id in pivot table
I have user table which have the default columns generated by:
php artisan make:auth
and messages tables which have the following columns:
id, from, message_content, group_id
and the group table have the columns:
id,type
now there is many to many relation between the users and groups table through the custom pivot table which have the columns:
group_id,user_id,last_id_seen
now if I want to retrieve the messages which belong to same group and have larger id than last_id_seen in the pivot table how to do it?
I think you are looking for something like this:
$groupId = 1; // set to whatever you want
$lastSeenId = \Auth::user()->groups()
->where('group_id', $groupId)
->first()->pivot->last_id_seen;
$messages = Message::where('id', '>', $lastSeenId)->get();
A more robust version, which does not fail when your user does not have an entry for the group yet, would be:
$groupId = 1; // set to whatever you want
$group = \Auth::user()->groups()->where('group_id', $groupId)->first();
$lastSeenId = $group ? $group->pivot->last_id_seen : null;
$messages = Message::when($lastSeenId, function($query, $id) {
$query->where('id', '>', $id);
})->get();
Note: normally you'd use optional() in the second snippet, but Laravel 5.2 does not come with this helper...
If you want both the count() of the results and the results themselves, you can store the query in a variable and perform two queries with it. You then don't have to rewrite the same query twice:
$groupId = 1; // set to whatever you want
$group = \Auth::user()->groups()->where('group_id', $groupId)->first();
$lastSeenId = $group ? $group->pivot->last_id_seen : null;
$query = Message::when($lastSeenId, function($query, $id) {
$query->where('id', '>', $id);
});
$count = $query->count();
$messages = $query->get();

Adding 'orWhere' to laravel eloquent model relationship

I have a model relation like the below one in User model,
public function notifications(){
return $this->morphMany('App\Notification','receiver')
->orWhere('type', 2);
}
This works well if there are no other conditions. But if $user is an instance of User and i call like, $user->notifications->where('status', 1);,
query condition becomes,
select * from notifications where notifications.receiver_id = 3
and notifications.receiver_id is not null and
notifications.receiver_type = User or type = 2 and status = 1
here the problem is, the final condition is working as an OR since i'm not able to group the relation's conditions. Therefore, even if the base relationship condition fails, but just the status & type fields match, it will fetch that record.
What i need is to add a parenthesis before the first where and close after the relation conditions, so that any other where condition applied will not affect the base query.
What i need:
select * from notifications where (notifications.receiver_id = 3
and notifications.receiver_id is not null and
notifications.receiver_type = User or type = 2 ) and status = 1
Check out Parameter Grouping.
Something like this:
Notifications::where('receveier_id', '=', 3)
->whereNotNull('receveier_id')
->where(function ($query) {
$query->where('receiver_type', '=', 'User')
->orWhere('type', '=', 2);
})
->where('status', '=', 1);
Try this..
public function notifications(){
return $this->where(function($query){
$query->morphMany('App\Notification','receiver')
->orWhere('type', 2);
})
}
Replace your code with this code in Model
Hope this works

codeigniter active record get last 10 records and sort ASC

I would like to know if this SQL statement is done in Codeigniter active record.
SELECT * FROM (
SELECT *
FROM chat
WHERE (userID = $session AND toID = $friendID)
OR (userID = $friendID AND toID = $session)
ORDER BY id DESC
LIMIT 10
) AS `table` ORDER by id ASC
You have to use the _compile_select() and _reset_select() methods of the active record class.
$this->db->select('*');
$this->db->from('chat');
$this->db->where("(userID='{$session}' AND toID='{$friendID}')");
$this->db->or_where("(userID='{$friendID}' AND toID='{$session}')");
$this->db->order_by('id', 'DESC');
$this->db->limit('10');
$subQuery = $this->db->_compile_select();
$this->db->_reset_select();
$this->db->select('*');
$this->db->from("{$subQuery} AS table");
$this->db->order_by('id', 'ASC');
$query = $this->db->get();
Unfortunately, in CI 2.0+, _compile_select() and _reset_select() are protected methods. Bummer. You'll have to follow this tutorial on extending the DB driver where you can write methods like the following:
function get_compiled_select()
{
return $this->db->_compile_select();
}
function do_reset_select()
{
$this->db->_reset_select();
}
I would like to take the time to point out that this type of action would be better served by joins. You should consider changing your db structure so that joins are possible and efficient.
This will work, though is not the active record variant.
$this->db->query( "SELECT * FROM (
SELECT *
FROM chat
WHERE (userID = ? AND toID = ?)
OR (userID = ? AND toID = ?)
ORDER BY id DESC
LIMIT 10
) AS `table` ORDER by id ASC", array( $session, $friendID, $friendID, $session) );"
You can use query as:
$this->db->select('SELECT *
FROM chat
WHERE (userID = $session AND toID = $friendID)
OR (userID = $friendID AND toID = $session)
ORDER BY id DESC
LIMIT 10') AS `table` ORDER by id ASC', FALSE);

Resources