laravel unique() dont work with paginating - laravel

i'm facing with an issue in laravel 5.7.28, i'm trying to get records with all of my fields and paginate them , sadly i got a bug in my code that cause registering users with duplicate phone number(a user registered itself more than one time and of course i fix the bug ) , for now i'm facing with a scenario that i want to fetch my (non-duplicate) distinct users on their phone, and paginate them . here is wishful scenario :
$users = User::distinct('phone')
->doesntHave('carrierUsers')
->doesntHave('thirdparty')
->latest()->paginate(40);
it returns all users (with duplicate phone number), seems distinct function not affected , so i tried unique function :
$users = User::doesntHave('carrierUsers')
->doesntHave('thirdparty')
->latest()->paginate(40)->unique('phone');
it works (for getting non-duplicate) but break paginating functionality
and for
$users = User::doesntHave('carrierUsers')
->doesntHave('thirdparty')
->latest()
->unique('phone')
->paginate(40);
i got error for undefined function on builder , means it works on collections ,
also i tried groupBy (search whole the web for solution) , it makes me confused till when i use it
User::doesntHave('carrierUsers')->doesntHave('thirdparty')
->latest()->groupBy('phone')
->paginate(40);
i got SELECT list is not in GROUP BY clause, is that mean in every phrase in my select statement (for now is all ('*') ) i should use it in my groupBy ?
i mean i need all columns , how can i have all those column in groupBy clause ? whats that mean by the way ?
, error is telling : this is incompatible with sql_mode=only_full_group_by. is she making this happen ? how can i fix it ?
1) excuse me for my english
2) Thanks for reading whole this
3) pleasure to reply and help me

Problem 1: Use distinct
For distinct() method, passing parameters is not supporteds, so distinct(phone) will not works.
You can use ->select('phone')->distinct() instead. However, support unique column is selected.
Problem 2: Use Group By
So if you want to select all columns and unique one column, you can use group by.
And because you are using mysql 5.7+.
The ONLY_FULL_GROUP_BY and STRICT_TRANS_TABLES modes were added in MySQL 5.7.5.
So if you select all columns and group one column, you will get the only_full_group_by error.
There are two options can solve this problem.
Solution1:
Use ANY_VALUE()
User::doesntHave('carrierUsers')->doesntHave('thirdparty')
->latest()
->selectRaw('phone, ANY_VALUE(id) AS id, ANY_VALUE(username) AS username, ...')
->groupBy('phone')
->paginate(40);
Solution2:
In config/database.php configuration, set strict mode to false(This is not very safty):
'mysql' => [
'driver' => 'mysql',
...
'strict' => false,
],

Related

Laravel: query with a different where condition in a different row

I have a classrooms table with a "quota" column that has different values. In one class, there are many students. How to display classrooms data with where condition which total students per row < "quota" ? Here's the table : .
Code :
Classroom::with('subject.teacher')->with('students')->whereHas('subject', fn ($query) => $query->where('grade', $grade))->withCount('students')->having('students_count', '<', 'quota');
when I use this code the result is empty
when "having" is removed this is the result :
The desired result only displays 3 classroom
You can use the has and a DB::raw.
// Simplified version
Classroom::has('students', '<=', DB::raw('classrooms.quota'))->get();
The withCount function appends an additional select column to your query. It does not use GROUP BY which is typically what is used in conjunction with having.
You can't actually filter by subqueries that are added in the select. However fortunately Laravel allows you to select rows based on how many related models they have using has. This query is also added as a subquery, but within the where clauses so you can also use column names within it like below:
Classroom::with('subject.teacher')
->with('students')
->whereHas('subject', fn ($query) => $query->where('grade', $grade))
->has('students', '<', \DB::raw('quota'))
->withCount('students');

Laravel error when get data from mySQL using groupBy

I want to get Data from mySQL where its only get 1 id_product,so when there is 2 data with same id_product value.. its only get the first one.I have tried the query in mySQL and its work, the query is like this
select `product_photo`.*, `product`.`id_merchant` from `product_photo` inner join `product` on
`product_photo`.`id_product` = `product`.`id` where `product`.`id_merchant` = 11 group by
`product_photo`.`id_product`
but i got error when i implemented it to my laravel controller,the code is like this
$id_merchant = $request->input('id_merchant');
$photo=DB::table('product_photo')
->join('product','product_photo.id_product','=','product.id')
->select('product_photo.*','product.id_merchant')
->where('product.id_merchant','=',$id_merchant)
->groupBy('product_photo.id_product')
->get();
return response()->json(['success'=>true,'message'=>'success', 'data' => $photo],200);
Can someone tell me why my laravel got error ? Sorry my english
This is caused by MySQL's strict mode. Change strict to false in your config/database.php file.
When you open the file, in the mysql array, set strict => false. After you disable MySQL's strict mode it shouldn't show the error anymore.

Laravel validation: unique with multiple columns and soft_delete

I am trying to do a Laravel validation rules as follow:
"permalink" => "required|unique:posts,permalink,hotel_id,deleted_at,NULL|alpha_dash|max:255",
The explanation to the rules is:
I have a table "Posts" in my system with the following fields (among others): hotel_id, permalink, deleted_at. If MySQL would allow make an unique index with null values, the sql would be:
ALTER TABLE `posts`
ADD UNIQUE `unique_index`(`hotel_id`, `permalink`, `deleted_at`);
So: I just add a new row IF: the combination of hotel_id, permalink and deleted_atfield (witch must be NULL) are unique.
If there is already a row where the permalink and hotel_id field are the same and 'deleted_at' field is NULL, the validation would return FALSE and the row wouldnt be inserted in the database.
Well. I don't know why, but the query Laravel is building looks like:
SELECT count(*) AS AGGREGATE FROM `posts`
WHERE `hotel_id` = the-permalink-value AND `NULL` <> deleted_at)
What the heck...
The query I was hoping Laravel build to validation is:
SELECT count(*) AS AGGREGATE FROM `posts`
WHERE `permalink` = 'the-permalink-value' AND `hotel_id` = ? AND `deleted_at` IS NULL
Could someone explain me how this effectively works? Because everywhere I look it looks like this:
$rules = array(
'field_to_validate' =>
'unique:table_name,field,anotherField,aFieldDifferentThanNull,NULL',
);
Does anyone could help me?
Thank you
all.
Finally, I got a proper understanding of the validation (at least, I think so), and I have a solution that, if it is not beautiful, it can helps someone.
My problem, as I said before, was validate if a certain column (permalink) is unique ONLY IF other columns values had some specific values. The problem is the way Laravel validation string rules works. Lets get to it:
First I wrote this:
"permalink" => "required|unique:posts,permalink,hotel_id,deleted_at,NULL|alpha_dash|max:255",
And it was generating bad queries. Now look at this:
'column_to_validate' => 'unique:table_name,column_to_validate,id_to_ignore,other_column,value,other_column_2,value_2,other_column_N,value_N',
So. The unique string has 3 parameters at first:
1) The table name of the validation
2) The name of the column to validate the unique value
3) The ID of the column you want to avoid (in case you are editing a row, not creating a new one).
After this point, all you have to do is put the other columns in sequence like "key,value" to use in your unique rule.
Oh, easy, an? Not so quickly, paw. If you're using a STATIC array, how the heck you will get your "currently" ID to avoid? Because $rules array in Laravel Model is a static array. So, I had to came up with this:
public static function getPermalinkValidationStr() {
$all = Input::all();
# If you are just building the frozenNode page, just a simple validation string to the permalink field:
if(!array_key_exists('hotel', $all)) {
return 'required|alpha_dash|max:255';
}
/* Now the game got real: are you saving a new record or editing a field?
If it is new, use 'NULL', otherwise, use the current id to edit a row.
*/
$hasId = isset($all['id']) ? $all['id'] : 'NULL';
# Also, check if the new record with the same permalink belongs to the same hotel and the 'deleted_at' field is NULL:
$result = 'required|alpha_dash|max:255|unique:posts,permalink,' . $hasId . ',id,hotel_id,' . $all['hotel'] . ',deleted_at,NULL';
return $result;
}
And, in the FrozenNode rules configuration:
'rules' => array(
'hotel_id' => 'required',
'permalink' => Post::getPermalinkValidationStr()
),
Well. I dont know if there is a easiest way of doing this (or a much better approach). If you know something wrong on this solution, please, make a comment, I will be glad to hear a better solution. I already tried Ardent and Observer but I had some problems with FrozenNode Administrator.
Thank you.

Laravel Eloquent - distinct() and count() not working properly together

So I'm trying to get the number of distinct pids on a query, but the returned value is wrong.
This is what I try to do:
$ad->getcodes()->groupby('pid')->distinct()->count()
what returns the value "2", while the value it should return, should be "1".
As a workaround, I'm doing this:
count($ad->getcodes()->groupby('pid')->distinct()->get())
what works fine and returns "1"
Is there any rule where count and distinct cannot be on the same query? I find the workaround kind of "heavy", I would like to make the original query work :(
The following should work
$ad->getcodes()->distinct()->count('pid');
A more generic answer that would have saved me time, and hopefully others:
Does not work (returns count of all rows):
DB::table('users')
->select('first_name')
->distinct()
->count();
The fix:
DB::table('users')
->distinct()
->count('first_name');
Anyone else come across this post, and not finding the other suggestions to work?
Depending on the specific query, a different approach may be needed. In my case, I needed either count the results of a GROUP BY, e.g.
SELECT COUNT(*) FROM (SELECT * FROM a GROUP BY b)
or use COUNT(DISTINCT b):
SELECT COUNT(DISTINCT b) FROM a
After some puzzling around, I realised there was no built-in Laravel function for either of these. So the simplest solution was to use use DB::raw with the count method.
$count = $builder->count(DB::raw('DISTINCT b'));
Remember, don't use groupBy before calling count. You can apply groupBy later, if you need it for getting rows.
You can use the following way to get the unique data as per your need as follows,
$data = $ad->getcodes()->get()->unique('email');
$count = $data->count();
Hope this will work.
I had a similar problem, and found a way to work around it.
The problem is the way Laravel's query builder handles aggregates. It takes the first result returned and then returns the 'aggregate' value. This is usually fine, but when you combine count with groupBy you're returning a count per grouped item. So the first row's aggregate is just a count of the first group (so something low like 1 or 2 is likely).
So Laravel's count is out, but I combined the Laravel query builder with some raw SQL to get an accurate count of my grouped results.
For your example, I expect the following should work (and let you avoid the get):
$query = $ad->getcodes()->groupby('pid')->distinct();
$count = count(\DB::select($query->toSql(), $query->getBindings()));
If you want to make sure you're not wasting time selecting all the columns, you can avoid that when building your query:
$query = $ad->select(DB::raw(1))->getcodes()->groupby('pid')->distinct();
I came across the same problem.
If you install laravel debug bar you can see the queries and often see the problem
$ad->getcodes()->groupby('pid')->distinct()->count()
change to
$ad->getcodes()->distinct()->select('pid')->count()
You need to set the values to return as distinct. If you don't set the select fields it will return all the columns in the database and all will be unique. So set the query to distinct and only select the columns that make up your 'distinct' value you might want to add more. ->select('pid','date') to get all the unique values for a user in a day
Based on Laravel docs for raw queries I was able to get count for a select field to work with this code in the product model.
public function scopeShowProductCount($query)
{
$query->select(DB::raw('DISTINCT pid, COUNT(*) AS count_pid'))
->groupBy('pid')
->orderBy('count_pid', 'desc');
}
This facade worked to get the same result in the controller:
$products = DB::table('products')->select(DB::raw('DISTINCT pid, COUNT(*) AS count_pid'))->groupBy('pid')->orderBy('count_pid', 'desc')->get();
The resulting dump for both queries was as follows:
#attributes: array:2 [
"pid" => "1271"
"count_pid" => 19
],
#attributes: array:2 [
"pid" => "1273"
"count_pid" => 12
],
#attributes: array:2 [
"pid" => "1275"
"count_pid" => 7
]
$solution = $query->distinct()
->groupBy
(
[
'array',
'of',
'columns',
]
)
->addSelect(
[
'columns',
'from',
'the',
'groupby',
]
)
->get();
Remember the group by is optional,this should work in most cases when you want a count group by to exclude duplicated select values, the addSelect is a querybuilder instance method.
Wouldn't this work?
$ad->getcodes()->distinct()->get(['pid'])->count();
See here for discussion..
Distinct do not take arguments as it adds DISTINCT in your sql query, however, you MAY need to define the column name that you'd want to select distinct with. Thus, if you have
Flight->select('project_id')->distinct()->get() is equialent to SELECT DISTINCT 'project_id' FROM flights and you may now add other modifiers like count() or even raw eloquent queries.
Use something like this
DB::table('user_products')->select('user_id')->distinct()->pluck('user_id')->toArray();
This was working for me so
Try This:
$ad->getcodes()->distinct('pid')->count()
try this
$ad->getcodes()->groupby('pid')->distinct()->count('pid')

Laravel 4: making a combination of values/columns unique

I'm importing a bunch of csv entries in my database with Laravel 4.
I can't really point at one column that has to be unique, it's a combination of 5 columns that makes it unique. However: how does one define this in Laravel?
Option 1: schema builder
You can use the $table->unique('email') method, but that only seems to allow one column, not a combination of columns.
Option 2: Validation
Less preferable, but I could validate the model before inserting it. However, again, using 'unique:[table]' validation rules, it will return an error when just one of the column values isn't unique, not a combination of them.
Can anyone tell me how I should go about this?
I'm sure I'm missing something, but I could use a push in the right direction :-)
Thanks,
Dieter
You can combine:
$table->unique( array('email','name') );
And pretty much everything in Laravel will accept arrays to do whatever you need to with 'more than one'.
Use Schema Builder's unique() method to define your data model, as Antonio mentioned.
Additionally, if you want to use validation on your model, consider my custom Validator rule for multiple UNIQUE indexes: https://github.com/felixkiss/uniquewith-validator
You can also do this;
$table->unique(["column1", "column2"], 'uq_columns');
Which means that you will have a unique column combination of all the columns i.e. column1 and column2
I know this question is for Laravel 4, but I just came across this on searches and found a solution for Laravel >= 5.3
Here it is:
Of course, the migration may look something like
$table->unique( array('email','name') );
Then to validate this, you do not need to use custom rules, just advanced rules:
'email' => Rule::unique('users')->where(function ($query) use ($request) {
return $query->where('name', $request->name);
}),
Of course, you may want to validate name before of this. The name should be required so that you may finish with something like this:
'name' => 'required|max:255',
'email' => Rule::unique('users')->where(function ($query) use ($request) {
return $query->where('name', $request->name);
}),
I hope it helps.
You can try this
$table->string("name");
$table->string("email")->unique("name")

Resources