Laravel Relation where not in - php

I have following table
users
id | username | password
1 | scott | 98746
2 | mark | 6542
3 | michel | 6589
user_detail
id | user_id | status | mobile_number
1 | 1 | pending | 987643210
2 | 2 | review | 3216547901
Now i want to retrieve those record where user has no records in user_detail table where status=pending
I have tried using relations in latest version
$user=User::with('userDetail')
->whereDoesntHave('userDetail',function ($query){
$query->where('status','pending');
})->get();
Same logic i am looking for without relations in laravel.Since we are using old laravel version which doesn't support.

I do not know what you mean by there are no relations, but if you have to do it with plain SQL, the query will look something like this:
$qry = "SELECT * FROM users WHERE id not in (SELECT u.id FROM users u INNER JOIN user_details d ON (u.id = d.user_id AND d.status = 'pending'));"
Then you can run the query by calling:
$results = DB::select($qry);
PS: Since I was not able to find it in the old docs, DB::select() may require 2nd param. If that is the case, just pass null as the second parameter.
EDIT:
I am not sure if this will work, and since it is for an old version, I am unable to test it, but something similar should work:
$rest = User::whereNotIn('id', function($q){
$q->select('user_id')->from('user_detail')->where('status','pending');
});

I believe you can do this by using left joins, something like this:
$rest = DB::table('users')
->leftJoin('userDetail','users.id','=','userDetail.user_id')
->whereNull('userDetail.id')
->orWhere('userDetail.status','=','pending')
->get();
If you need the eloquent collection of Users, you can use the hydrate method like this:
$rest = User::hydrate(DB::table('users')
->leftJoin('userDetail','users.id','=','userDetail.user_id')
->whereNull('userDetail.id')
->orWhere('userDetail.status','=','pending')
->select('users.*')
->get()->toArray());
cheers!

Related

Doctrine Query how many times an Entity appears on other table

Suppose I have two tables:
Customer
id | name
1 | John
2 | Chris
Sales
id | client_id | price
1 | 1 | 100
2 | 1 | 200
3 | 1 | 300
4 | 2 | 150
5 | 2 | 250
The relationship between the tables is 1:M
What my query should look like if I want to return the data below?
client_name | number of sales
John | 3
Chris | 2
Right now my querybuilder looks like this, just a simple select *
$objs = $this->getDoctrine()->getManager()
->getRepository(Customer::class)
->createQueryBuilder('obj');
$objs = $objs->getQuery()->getResult();
Should I use some sort of join, or subquery? I would appreciate a little guidance, thank you.
Basically what #Arno Hilke wrote, but with some changes (and assuming your Customer entity is actually named Client):
$query = $this->getDoctrine()->getManager()
->getRepository(Client::class)
->createQueryBuilder('c')
->select('c.name as client_name, COUNT(s.client) as number_of_sales')
->join('c.sales', 's')
->groupBy('s.client')
->getQuery();
$result = $query->getArrayResult();
Join your sales table. Group by customers and count the occurrences of each customer. Something like this should work, depending on your exact entity definitions:
$query = $this->getDoctrine()->getManager()
->getRepository(Customer::class)
->createQueryBuilder('customer')
->select('customer.id as id, count(customer.id) as number');
->join('customer.sales', 'sales')
->groupBy('sales');
$result = $query->getQuery()->getArrayResult();
Thank you #Arno Hilke and #MichaƂ Tomczuk.
I changed the code a little to fit my needs. I needed some conditions in the select, so instead of COUNT I used SUM, the code ended like this:
$query = $this->getDoctrine()->getManager()
->getRepository(Customer::class)
->createQueryBuilder('c')
->select("c.name, SUM(CASE WHEN s.conditioneOne = 'valueOne' AND s.conditionTwo = 'valueTwo' THEN 1 ELSE 0 END) AS number_of_sales")
->join('c.sales', 's')
->groupBy('s.costumer')
->orderBy('number_of_sales', 'DESC');
$results = $query->getQuery()->getArrayResult();

Laravel 5.6 Eloquent ORM where join table

I am dealing with Eloquent ORM collections and query builders. I am trying to figure out how to join and use "where" in a collection, like in query builder.
For example, I have the following tables:
Users:
ID | Name | Last name
-------------------------
1 | Martin | Fernandez
2 | Some | User
Persons:
ID | Nick | User_ID | Active
----------------------------------
1 | Tincho | 1 | 1
Companies:
ID | Name | User_ID | Active
----------------------------------
1 | Maramal| 1 | 0
2 | Some | 2 | 1
This is an example, the tables I am working on have more than 30 columns each one. I want to select all the user that are active.
Usually I would do a query like:
SELECT *
FROM users
LEFT JOIN persons ON users.id = persons.user_id
LEFT join companies ON users.id = companies.user_id
WHERE persons.active = 1
OR companies.active = 1
That can be translated to Laravel Query Builder like:
DB::table('users')
->leftJoin('persons', 'users.id', '=', 'persons.user_id')
->leftJoin('companies', 'users.id', '=', 'companies.user_id')
->where('persons.active', 1)
->orWhere('companies.active', 1)
->get();
But what I want to use is a Laravel Eloquent ORM Collection, until now I am doing the following:
$users= User::orderBy('id',' desc')->get();
foreach($users as $k => $user) {
if($user->company && !$user->company->active || $user->person && !$user->person->active) {
unset($users[$k]);
}
... and here a lot of validations and unsets ...
}
But I know that at this point, I already grabbed all the users instead those who are active.
How would I achieve what I did with query builder within a collection? Thanks in advance.
This should do it:
$users = User::whereHas('companies', function($q) {
$q->where('active', true);
})->orWhereHas('persons', function($q) {
$q->where('active', true);
})->with(['companies', 'persons'])->orderBy('id', 'desc')->get();

Converting raw queries into laravel

I have this schema
product_categories
id | product_category
---------------------
1 | ABC
2 | DBC
3 | EBA
store_product_categories
id | category_id | store_id
------------------------
1 | 2 | 11
2 | 1 | 11
3 | 3 | 11
I have created a query in mysql work bench
SELECT pc.* FROM product_categories pc LEFT JOIN store_product_categories spc ON pc.category = pc.id AND spc.store_id = 11 WHERE spc.category IS NULL;
This query actually gets all those categories from product_categories table which are not present in store_product_categories.
Now I am really really confused how to build this is Laravel Eloq..
I did try this.
$exclusive_categories = Product_category::join('store_product_categories','store_product_categories.category_id','=','product_categories.id')
->where('store_product_categories.store_id','=',session('store_id'))
->where('store_product_categories.category_id','=','NULL')->get();
But this doesn't give me result
Since you're joining on two different columns, you need to pass that through a function/closure:
$exclusive_categories = Product_category::leftJoin('store_product_categories', function($join) {
$join->on('store_product_categories.category_id','=','product_categories.id')
->on('store_product_categories.store_id','=',session('store_id'));
})
->where('store_product_categories.store_id','=','NULL')->get();
I'm not sure if this is quite what you want. If you're looking for where store_id is NULL OR store_id = the session id, you can pass that through another closure/function.
$exclusive_categories = Product_category::leftJoin('store_product_categories spc', function ($join) {
$join->on('spc.category_id', '=', 'product_categories.id');
$join->on('spc.store_id', '=', \DB::raw(session('store_id')));
})
->whereNull('spc.category_id')
->get(['product_categories.*']);
$exclusive_categories = Product_category::leftJoin('store_product_categories','store_product_categories.category_id','=','product_categories.id')
->where('store_product_categories.store_id','=',session('store_id'))
->whereNull('store_product_categories.store_id')->get();
https://laravel.com/docs/5.3/queries

Merge and Sort two Eloquent Collections?

I've two Collections and I want merge it to one variable (of course, with ordering by one collumn - created_at). How Can I do that?
My Controllers looks that:
$replies = Ticket::with('replies', 'replies.user')->find($id);
$logs = DB::table('logs_ticket')
->join('users', 'users.id', '=', 'mod_id')
->where('ticket_id', '=', $id)
->select('users.username', 'logs_ticket.created_at', 'action')
->get();
My Output looks for example:
Replies:
ID | ticket_id | username | message | created_at
1 | 1 | somebody | asdfghj | 2014-04-12 12:12:12
2 | 1 | somebody | qwertyi | 2014-04-14 12:11:10
Logs:
ID | ticket_id | username | action | created_at
1 | 1 | somebody | close | 2014-04-13 12:12:14
2 | 1 | somebody | open | 2014-04-14 14:15:10
And I want something like this:
ticket_id | table | username | message | created_at
1 |replies| somebody | asdfghj | 2014-04-12 12:12:12
1 | logs | somebody | close | 2014-04-13 12:12:14
1 | logs | somebody | open | 2014-04-14 11:15:10
1 |replies| somebody | qwertyi | 2014-04-14 12:11:10
EDIT:
My Ticket Model looks that:
<?php
class Ticket extends Eloquent {
protected $table = 'tickets';
public function replies() {
return $this->hasMany('TicketReply')->orderBy('ticketreplies.created_at', 'desc');
}
public function user()
{
return $this->belongsTo('User');
}
}
?>
A little workaround for your problem.
$posts = collect(Post::onlyTrashed()->get());
$comments = collect(Comment::onlyTrashed()->get());
$trash = $posts->merge($comments)->sortByDesc('deleted_at');
This way you can just merge them, even when there are duplicate ID's.
You're not going to be able to get exactly what you want easily.
In general, merging should be easy with a $collection->merge($otherCollection);, and sort with $collection->sort();. However, the merge won't work the way you want it to due to not having unique IDs, and the 'table' column that you want, you'll have to make happen manually.
Also they are actually both going to be collections of different types I think (the one being based on an Eloquent\Model will be Eloquent\Collection, and the other being a standard Collection), which may cause its own issues. As such, I'd suggest using DB::table() for both, and augmenting your results with columns you can control.
As for the code to achieve that, I'm not sure as I don't do a lot of low-level DB work in Laravel, so don't know the best way to create the queries. Either way, just because it's looking like starting to be a pain to manage this with two queries and some PHP merging, I'd suggest doing it all in one DB query. It'll actually look neater and arguably be more maintainable:
The SQL you'll need is something like this:
SELECT * FROM
(
SELECT
`r`.`ticket_id`,
'replies' AS `table`,
`u`.`username`,
`r`.`message`,
`r`.`created_at`
FROM `replies` AS `r`
LEFT JOIN `users` AS `u`
ON `r`.`user_id` = `u`.`id`
WHERE `r`.`ticket_id` = ?
) UNION (
SELECT
`l`.`ticket_id`,
'logs' AS `table`,
`u`.`username`,
`l`.`action` AS `message`,
`l`.`created_at`
FROM `logs` AS `l`
LEFT JOIN `users` AS `u`
ON `l`.`user_id` = `u`.`id`
WHERE `l`.ticket_id` = ?
)
ORDER BY `created_at` DESC
It's pretty self-explanatory: do the two queries, returning the same columns, UNION them and then sort that result set in MySQL. Hopefully it (or something similar, as I've had to guess your database structure) will work for you.
As for translating that into a Laravel DB::-style query, I guess that's up to you.

Exploding in php

In my table 'users' there are 'friends' ,
Like this :
+----+------+---------+
| id | name | friends |
+----+------+---------+
| 1 | a | 0,1,2 |
| 2 | b | 0,1,3 |
| 3 | c | 0,1 |
+----+------+---------+
How do I use the explode function to get the friends id one by one (not 0,1,2) that are separated by a comma (,) ;
How do I select the id? (Example) :
$sql = Select id from users where id = (exploded)
if (mysql_num_rows($sql) > 0 ) {
$TPL->addbutton('Unfriend');
}else{
$TPL->addbutton('Add as Friend')
}
The solution here is actually a slight change in your database structure. I recommend you create a "many-to-many" relational table containing all of the users friends referenced by user.
+---------+-----------+
| user_id | firend_id |
+---------+-----------+
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 1 |
| 2 | 5 |
+---------+-----------+
If you are storing lists of values within one field then that is the first sign that your database design is not quite optimal. If you need to search for a numerical value, it'll always be better to place an index on that field to increase efficiency and make the database work for you and not the other way around :)
Then to find out if a user is a friend of someone, you'll query this table -
SELECT * FROM users_friends WHERE
`user_id` = CURRENT_USER AND `friend_id` = OTHER_USER
To get all the friends of a certain user you would do this -
SELECT * FROM users_friends WHERE `user_id` = CURRENT_USER
Just a simple example that will make you clear how to proceed:
// Obtain an array of single values from data like "1,2,3"...
$friends = explode(',', $row['friends']);
Then, back in your query:
// Obtain back data like "1,2,3" from an array of single values...
$frieldslist = implode(',', $friends);
$sql = "SELECT * FROM users WHERE id IN ('" . $frieldslist . "')";
to get an array of if ids from your string explode would be used like this
$my_array = explode("," , $friends);
but you'd probably be better using the mysql IN clause
$sql = "Select id from users where id in (".$row['friends'].")";
Just a quick idea. Change your database's table. It is certain that after a while many problems will arise.
You could have something like this.
id hasfriend
1 2
1 3
2 1 no need to be here (You have this already)
2 4
.....
You can do this by using indexes for uniqueness or programming. You may think of something better. Change your approach to the problem to something like this.

Categories