Laravel Eloquent - Multiple NOT LIKE queries? - mysql

I have a changeable output from the database like this:
1,2,3,4 and sometimes 5,4,9
I want to add a not like where clause depending on the user:
For example:
User 1
$products = \App\Product::where('user', '1')
->where(['category', 'not like', '1,2,3,4')
->get();
User 2
$products = \App\Product::where('user', '2')
->where(['category', 'not like', '5,4,9')
->get();

https://laravel.com/docs/5.8/queries#where-clauses
$products = \App\Product::where('user','2')
->whereNotIn('category', [5, 4, 9])
->get();

If all you need is to change the category them why not make it a variable like this.
$categories = $userId == 1 ? [1,2,3,4] : [5,4,9];
$products = \App\Product::where('user',$userId)
->whereNotIn('category', $categories)
->get();
So my guess is that you need to have some method to see which categories a user should have that would be more complex than this, but well I hope you get the idea.

Another way
$user_id = $request->input('id');//Put your user id in variable either by request or anyway you want to save
$category_arr = [];
if($user_id == 1) {
$category_arr = [1,2,3,4];
} elseif ($user_id == 2) {
$category_arr = [5,4,9];
} else {
echo "Error Msg"
}
$products = \App\Product::where('user',user_id)
->whereNotIn('category', $category_arr)
->get();

This is an answer that extend the Roman Bobrik's an Eli's.
The goal is to let your request to be able to handle a default condition while handling specific request when you need to.
if ($request->input('id') == 1) {$categories = [1,2,3,4];}
if ($request->input('id') == 2) {$categories = [5,4,9];}
$products = \App\Product::where('user', $request->input('id'))
->when(isset($categories), function($query) use($categories) { // This will be applied for specified users. In this case, users id = 1 || 2
return $query->whereNotIn('category', $categories);
})->when(!isset($categories), function($query) { // This will be applied when users id != 1 || 2
return $query->whereNotIn('category', [1,2]); //Define your default categories for every users that are not specified at the begining.
})->get();
You could also save the categories array to the users table and change the when() condition for when(Auth::user()->categories != null) and when(Auth::user()->categories == null).
The only warning is, when you use that kind of query, you must have two when() condition that are the exact opposite. Because if none of the both condition are respected, the final query will be $products = \App\Product::where('user', $request->input('id'))->get(); and I'm sure that you won't that to happen.
Well, you can chain more than two when() condition, but be sure to have one that will catch everything that was not catch by any other.

If your changeable output is an array, you can try this:
$categories = [1, 2, 3, 4];
$products = \App\Product::where('user', 1)
->whereNotIn('category', $categories)
->get();

Related

How to limit the count of rows for a specific foreign key value in Laravel

We have a posts table and user_id is the foreign key
For example, I want to choose posts for these users
$users=[1,2,13,16,17,19];
$posts = Post::whereIn('user_id', $users)->paginate(10);
But I want the user 1 and 2 only have two posts in the output, for the rest of the users there is not a limited number of posts.
Note: User 1 and 2 are not always within the $users array, and due to the condition, one or both of them may not be in the array.
Do you have a solution for me?
You can't achieve this in a single query, we need to take it separately like this
$users=[1,2,13,16,17,19];
// first take all the post except the two
$posts = Post::whereIn('user_id', $users)->whereNotIn('user_id', [1,2])->get()->toArray();
// then take one user 1 post in desc and limit it by 2
$userOnePost = Post::whereIn('user_id', $users)->where('user_id', 1)->limit(2)->orderBy('post.id', 'desc')->get()->toArray();
// then take one user 2 post in desc and limit it by 2
$userTwoPost = Post::whereIn('user_id', $users)->where('user_id', 2)->limit(2)->orderBy('post.id', 'desc')->get()->toArray();
// merge all the array
$allPost = array_merge(posts,$userOnePost,userTwoPost);
You can try this alternative :
$posts = [];
$users=[1,2,13,16,17,19];
$userWithJustTwo = [1,2];
$result = array_intersect($users, $userWithJustTwo);
$posts[] = Post::whereIn('user_id', $result)->orderBy('created_at', 'desc')->take(2)->get();
$array = array_diff($users, userWithJustTwo);
$posts[] = Post::whereIn('user_id', $array)->get();
You can do this by using ->take()
$posts = [];
$users=[1,2,13,16,17,19];
foreach($users as $user)
{
if($user == 1 || $user == 2)
{
$posts[] = Post::where('user_id', $user)->take(2)->get();
}
else
{
$posts[] = Post::where('user_id', $user)->get();
}
}
Read more at https://laravel.com/docs/5.7/queries
try this method:
$users = User::whereNotIn('id', [1,2])->pluck('id');
$posts = [];
$posts[] = Post::whereIn('user_id', [1,2])->take(2)->get();
$posts[] = Post::whereIn('user_id', $users)->get();
if you want to get the latest post use this instead:
$posts[] = Post::whereIn('user_id', [1,2])->latest('created_at')->take(2)->get();
you may also use Parameter Grouping like this
DB::table('post')
->whereIn('user_id', $users)
->where(function ($query) {
$query->whereIn('user_id', [1,2])->limit(2)
->orWhereNotIn('user_id', [1,2]);
})
->get();

MySql multiple times Where in Condition if possible

i am Performing the MySQL query with Where In conditions.
Here is my Query.
The query should be:
SELECT * FROM users WHERE id IN (44,44,33,44,33,0);
Query showing my correct result, no problem at all,
but what i want to do is Can we Divide all the id with the individual conditions ?
Or can a query has multiple Where In for a single column?
like
SELECT * FROM users WHERE id IN (44)
AND id IN (45)
AND id IN (46);
like this.
Is that possible ? ?
My query code for performing the query, its in Laravel.
$films = Film::with('genre')->with('languages')->with('likes')->with('comments')->with('likedBy');
if(Input::get('sort')){
$sort = Input::get('sort');
switch ($sort){
case 'old_new':
$films = $films->orderBy('created_at', 'asc');
break;
case 'new_old':
$films = $films->orderBy('created_at', 'desc');
break;
case 'views':
// $films = DB::table('films')
// ->leftJoin('film_views', 'films.id', '=', 'film_views.film_id')
// ->select(DB::raw('films.*, count(film_views.film_id) as views'))
// ->groupBy('films.id')
// ->orderBy('views' , 'desc')
// ;
$films = $films
->leftJoin('film_views', 'films.id', '=', 'film_views.film_id')
->select(DB::raw('films.*, count(film_views.film_id) as views'))
//->whereBetween('created_at', [$this->first_day_of_search, $this->final_day_of_search])
->groupBy('films.id')
->orderBy('views' , 'desc')
;
break;
case 'likes':
$films = $films
->leftJoin('film_likes', 'films.id', '=', 'film_likes.film_id')
->select(DB::raw('films.*, count(film_likes.film_id) as likes'))
->groupBy('films.id')
->orderBy('likes' , 'desc')
;
break;
}
}
if(Input::get('filter')) {
$jsonFilter = Input::get('filter');
$filters = json_decode($jsonFilter);
foreach ($filters as $filter => $value){
switch ($filter){
case "genre":
if($value){
$films = $films->whereHas('genre', function ($query) use($value) {
$query->whereIn('genre_id', $value);
});
}
break;
case "cert":
if($value){
$films = $films->whereIn('cert', $value);
}
break;
case "country":
if($value){
$films = $films->whereIn('country', $value);
}
break;
case "lang":
if($value){
$films = $films->whereHas('languages', function ($query) use($value) {
$query->whereIn('language_id', $value);
});
}
break;
}
}
}
$films = $films->paginate(5);
return parent::api_response($films->toArray(), true, ['return' => 'all films'], 200);
I will post an answer if for no other reason than amusement. I believe that a WHERE IN clause is internally converted into a series of equality onditions separated by OR. So your original query
SELECT * FROM users WHERE id IN (44,44,33,44,33,0)
would be internally converted to this
SELECT * FROM users WHERE id = 44 OR id = 44 OR id = 33 OR id = 44 OR
id = 33 OR id = 0
So having a single number for your IN clause would be equivalent to a single equality condition.
By the way, you have the same numbers appearing multiple times, which doesn't make any sense.
Yes, it is possible to have multiple WHERE IN.
SELECT * FROM users WHERE id IN (44) OR id IN (45) OR id IN (46);
This is correct.
However I don't understand why you want to do this because this is low in performance compared to having one IN

Symfony2 Doctrine error: Cannot count query that uses a HAVING clause. Use the output walkers for pagination

I am trying to get collections that are non-empty, i.e. have at least 1 object. Collection entity has OneToMany relationship with Object entity. I am using KNP paginator to paginate result. This is my function:
public function fetchAction(Request $request){
$em = $this->getDoctrine()->getManager();
$page = $request->get('page', 1);
$limit = 10;
$collections = $em->createQueryBuilder()
->select('c')
->add('from', 'CollectionBundle:Collection c LEFT JOIN c.object o')
->having('COUNT(o.id)>0')
->orderBy('c.date', 'DESC')
->getQuery();
$collections = $this->get("knp_paginator")->paginate($collections, $page, $limit);
return $this->render('CollectionBundle:Collection:fetch.html.twig', [
'collections' => $collections
]);
}
Error
I keep getting following error
Cannot count query that uses a HAVING clause. Use the output walkers for pagination
Without 'Having' clause everything works fine, but I must get non-empty collections.
wrap-queries solved this problem
$collections = $this->get("knp_paginator")->paginate($collections, $page, $limit,array('wrap-queries'=>true));
You can implement the Manual counting, as described here in the doc.
As example, you can modify your code as follow:
$count = $em->createQueryBuilder()
->select('COUNT(c)')
->add('from', 'CollectionBundle:Collection c LEFT JOIN c.object o')
->having('COUNT(o.id)>0')
->orderBy('c.date', 'DESC')
getSingleScalarResult();
$collections = $em->createQueryBuilder()
->select('c')
->add('from', 'CollectionBundle:Collection c LEFT JOIN c.object o')
->having('COUNT(o.id)>0')
->orderBy('c.date', 'DESC')
->getQuery();
$collections->setHint('knp_paginator.count', $count);
$collections = $this->get("knp_paginator")->paginate($collections, $page, $limit,array('distinct' => false));
return $this->render('CollectionBundle:Collection:fetch.html.twig', [
'collections' => $collections
]);
Hope this help
My solution is based on #Matteo's solution, since my query was a bit complicated I wanted to share my version also:
$qb = $this->createQueryBuilder('c');
$qb->select('count(c.id)')
->addSelect('COUNT(DISTINCT m.id) AS HIDDEN messageCount')
->addSelect('COUNT(DISTINCT f.id) AS HIDDEN fileCount')
->join('c.user', 'u')
->join('c.status', 's')
->join('c.company', 'comp')
->leftJoin('c.files', 'f')
->leftJoin('c.messages', 'm');
$this->_set_filters($filter, $qb);
$qb->groupBy('c.id');
$countQuery = $qb->getQuery();
/** wrap query with SELECT COUNT(*) FROM ($sql)
* I don't know what exactly does this block but
* I coppied it from Doctrine\ORM\Tools\Pagination\Paginator::getCountQuery()
*/
$platform = $this->getEntityManager()->getConnection()->getDatabasePlatform();
$rsm = new Query\ResultSetMapping();
$rsm->addScalarResult($platform->getSQLResultCasing('dctrn_count'), 'count');
$countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class);
$countQuery->setResultSetMapping($rsm);
return $countQuery->getSingleScalarResult(); //returns integer

Symfony 1.4 select query with selected columns is not working

I want to run following query in symfony doctrine.
SELECT p.id AS id FROM skiChaletPrice p WHERE ski_chalet_id = ? AND month = ?
I wrote my doctrine query as following.
$q = Doctrine_Query::create()
->select('p.id AS id')
->from('skiChaletPrice p')
->andWhere('ski_chalet_id = ?', $chaletId)
->andWhere('month = ?', $from);
$result = $q->fetchOne();
if ($result->count() > 0) {
return $result->toArray();
} else {
return null;
}
But my result always include all columns in the table. What the issue? Please help me.
The issue is that fetchOne() will return a Doctrine object, which implicitly contains all the columns in the table. $result->toArray() is converting that doctrine object to an array, which is why you get all the columns.
If you only want a subset of column, don't hydrate an object, instead do something like this:
$q = Doctrine_Query::create()
->select('p.id AS id')
->from('skiChaletPrice p')
->andWhere('ski_chalet_id = ?', $chaletId)
->andWhere('month = ?', $from);
$results = $q->execute(array(), Doctrine::HYDRATE_SCALAR);
See http://docs.doctrine-project.org/projects/doctrine1/en/latest/en/manual/data-hydrators.html
This is how I should do it:
$result = Doctrine_Query::create()
->select('id')
->from('skiChaletPrice')
->andWhere('ski_chalet_id = ?', $chaletId)
->andWhere('month = ?', $from)
->limit(1)
->fetchOne(array(), Doctrine_Core::HYDRATE_SINGLE_SCALAR);
// result will be a single id or 0
return $result ?: 0;
// if you want array($id) or array() inseatd
// return (array) $result;

How to use 'distinct' in zend db model

I have search for a long time to get this thing work.
What I want is to know how I user the 'distinct' in a zend db model to make my selection for the followers of a user unique.
My db model to count followers for a user (here I need to add the 'distinct')
public function countFollowers($user_id)
{
$rowset = $this->fetchAll("user_id = $user_id");
$rowCount = count($rowset);
if ($rowCount > 0) {
return $rowCount;
} else {
return $rowCount;
}
}
EDIT: This function is part of 'class Application_Model_DbTable_Followers extends Zend_Db_Table_Abstract'
My table structure
id
article_id // Id of the article who is written by 'user_id'.
user_id // user_id owner of the article
follower_id // member who has following this article
date // date of follow
'user_id' can be written various articles, the follower can follow various articles of the same writer. I want to make a unique follower count. As an example what I want, If a follower is following 8 articles of one writer it has to be compared to '1' in the count.
I hope this will be clear enough to understand what I tried to reach.
With kind regards,
Nicky
Using distinct:
public function countFollowers($user_id)
{
$select = $this->select()
->distinct()
->where('user_id = ?', $user_id);
$rowset = $this->fetchAll($select);
$rowCount = count($rowset);
return $rowCount;
}
EDIT: After edit in question to get count of followers of a user. You actually need to use group NOT distinct. I have tested the following query works to fetch the data to be count()ed,
SELECT * FROM followers WHERE user_id = 1 GROUP BY user_id,
follower_id
I have not tested the code, but something like this should work:
public function countFollowers($user_id)
{
$select = $this->select()
->where('user_id = ?', $user_id)
->group(array('user_id', 'follower_id'));
$rowset = $this->fetchAll($select);
$rowCount = count($rowset);
return $rowCount;
}
You can specify mysql functions in the 'from' function that makes up select query function. To use the from function you need to pass the table name as the first parameter, however passing $this (your table model class) works fine.
public function countFollowers($user_id)
{
$rowset = $this->fetchAll(
$this->select()
->from($this, array('DISTINCT user_id'))
->where('user_id = ?', $user_id)
);
return count($rowset);
}
[edit]
Based on your edit, 'group' may also work for you:
public function countFollowers($user_id)
{
$rowset = $this->fetchAll(
$this->select()
->where('user_id = ?', $user_id)
->group('user_id')
);
return count($rowset);
}
This will group all matching user_id into one record. So if a user is found, it will return 1, else 0.
Retrieving all the rows simply to get a count strikes me as overkill.
You can do a count using something like this:
$select = $db->select();
$select->from('testcount', new Zend_Db_Expr('COUNT(id)'))
->where('user_id = ?', $someUserId);
return $db->fetchOne($select);
don't write that :
public function countFollowers($user_id)
{
$rowset = $this->fetchAll(
$this->select()
->from($this, array('DISTINCT user_id'))
->where('user_id = ?', $user_id)
);
return count($rowset);
}
But that :
public function countFollowers($user_id)
{
$rowset = $this->fetchAll(
$this->select()
->from($this, array('DISTINCT(user_id)'))
->where('user_id = ?', $user_id)
);
return count($rowset);
}
Else you will have an error wich looks like to Mysqli prepare error:
Unknown column 'repertoire.distinct idRepertoireParent' in 'field list'
Also we have one method from the official manual
Just use "distinct"
Build this query: SELECT DISTINCT p."product_name" FROM "products" AS p
$select = $db->select()
->distinct()
->from(array('p' => 'products'), 'product_name');
Today I tried DISTINCT in JOIN LEFT case and it doesn't work. But if you add a Group By to the DISTINCT column, it works fine.