Laravel select columns and then group column value by date - php

I'm trying to get what should seem like a simple SQL query to work in my Laravel project, I've got a front-end web application that allows a user to pull data from various pre-defined tables and a selectable list of columns.
This works fine if I don't attempt to select and groupBy together, equally, I need someway of grouping baed on whether the user wants to group data by a day, or a month for instance.
My POST request looks like, where each array item inside the parent array is a table:
[
[
table: 'my_table',
columns: ['event_category', 'event_action', 'event_count', 'created_at'] ...
filterBy: [
['event_category', 'my category'],
['event_action', 'some action']
],
orderBy: {
field: 'created_at',
direction: 'desc'
}
]
]
Each row in my_table contains an event_count column, which contains a number, so if there's 5 rows for a particular day with different event_count numbers, I need to add up all of those event_count entries and group them by that day
Here's my function and query:
public function findDataFromSources(Request $request)
{
$request_data = $request->all();
$realData = [
'size' => 0,
'results' => [],
'filtered' => []
];
foreach ($request_data as $key => $findable) {
// NOTE: this works for retrieving data that isn't grouped
// $res = DB::table($findable['table'])
// ->select($findable['columns'])
// ->where($findable['filterBy'])
// ->orderBy($findable['orderBy']['field'], $findable['orderBy']['direction'])
// ->take(20)
// ->get();
// TODO: this isn't grouping for some reason...
$res = DB::table($findable['table'])
->select($findable['columns'], DB::raw('sum(event_count) as total_events'))
->groupBy('created_at')
->orderBy($findable['orderBy']['field'], $findable['orderBy']['direction'])
->get();
$realData['size'] += count($res);
array_push($realData['results'], $res);
}
$data = [
'success' => true,
'message' => 'Your chosen data sources and fields',
'sources' => $realData
];
}
What am I missing? The error I'm getting is:
SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column

->select($findable['columns']
You cannot SELECT fields/columns that are not in GROUP BY without aggregate function and your $findable['columns'] probably contain fields that are not in GROUP BY.
You can use ANY_VALUE() like #RobbinBenard commented for example
->select(DB::raw('ANY_VALUE(' .$findable['columns'][0] .')'))
You can loop through the columns and use addSelect() to add those columns.
$query = DB::table($findable['table'])
->select(DB::raw('sum(event_count) as total_events'));
foreach($findable['columns'] as $column) {
$query->addSelect(DB::raw('ANY_VALUE(' .$column .')'));
}
$res = $query->groupBy('created_at')
->orderBy($findable['orderBy']['field'], $findable['orderBy']['direction'])
->get();

Related

Get maximum values in column with diferents foreign ids in Laravel

I want to get the largest value from a column, but those values ​​correspond to two different foreign ids, so I want it to return the maximum number for each foreign id. I don't know If I have to use max()... At the moment I am just using groupBy with the two foreign ids.
Here the table in MySqlAdmin. I want to get the maximum bid for each id_group and id_karatekas:
And here is my code:
public function soldKarateka()
{
$response = array('code' => 400, 'error_msg' => []);
self::getBidByKaratekaGroup($response);
return response($response, $response['code']);
}
private function getBidByKaratekaGroup( & $response){
$bidsPerKarateka = DB::table('bids')
->groupBy('id_group','id_karatekas')
->get();
$response = array('code' => 200, 'Karatekas order by group' => $bidsPerKarateka);
}
You should try below code for find max value.
if your table name is bids then it will be ok otherwise change name as per your table name.
DB::table('bids')
->select([DB::raw('MAX(bids.bid) AS maxbid'), 'bids.id_group', 'bids.id_karatekas'])
->groupBy('bids.id_group', 'bids.id_karatekas')
->get()->toArray();
Did you try the max function?
I think the following snippet will work
$data = DB::table('bids')
->select(DB::raw('MAX(bid) as bid'))
->groupBy('id_group', 'id_karatekas')->get();

Laravel Validate if there is already a record with similar combination of two columns

For example, I have an auction where Users can bid on items.
When a user creates a new bid I want to be sure that this user didn't bid on this item before.
The BID table structure is id, item_id, user_id, price. So basically I need to check if there is a record that has same item_id and same user_id at the same time.
Is there any way to do this using Laravels Validator?
$validator = Validator::make($request->all(), [
'item_id' => 'unique:item_id,user_id,NULL,id,'. auth()->user()->id,
'price' => 'required|integer'
]);
The error I have:
Undefined offset: 1
According to Laravel docs:
[
'item_id' => 'unique:bids,item_id,null,id,user_id,' . auth()->id(),
]
Create a model for Users table.
Now,
$itemIds= User:: where ('user_id',$userId)->pluck('item_Id');
foreach($itemIds as $itemId){
if ($itemId == $newItemId) {
// write code if item id is already present
}
else {
// write code if item id is not present
}
}
you can use laravel unique validation as
'user_id' => 'unique:item_id,user_id,NULL,id,'.$user->id

Laravel : How to exclude duplicate records when inserting a group of data into mysql?

I am using Laravel 5.5,I want to exclude duplicate records when inserting a group of data into mysql.
For example,there is a table students,it has these fields:
id
name
gender
Now I will insert a group of data into students,if not mind duplicate records,I can do it like this:
public function insert()
{
$newStudents=[
['name'=>'Jim','gender'=>'boy'],
['name'=>'Lucy','gender'=>'girl'],
['name'=>'Jack','gender'=>'boy'],
['name'=>'Alice','gender'=>'girl']
];
DB::table('students')->insert($newStudents);
}
Now I don't want to insert duplicate records.(The duplicate is : both name and gender have the same values,not one field has the same value).
what should I do?
You could use the collection helper unique. See code below:
$newStudents=collect([
['name'=>'Jim','gender'=>'boy'],
['name'=>'Lucy','gender'=>'girl'],
['name'=>'Jack','gender'=>'boy'],
['name'=>'Alice','gender'=>'girl'],
['name'=>'Alice','gender'=>'girl']
])->unique(function ($student) {
return $student['name'].$student['gender'];
})->toArray();
DB::table('students')->insert($newStudents);
The above code will only insert unique records, even though there is a duplicate record there.
For more information, see here:https://laravel.com/docs/5.4/collections#method-unique
You could create an unique index for the name and gender in the database. However, then when you try to save them you will get a MySQL error I'm guessing. So you could use the unique validation with where.
Validator::make($student, [
'gender' => [
'required',
Rule::in(['boy', 'girl']),
],
'name' => Rule::unique('students')->where(function ($query) use ($student) {
$query->where('gender', $student['gender']);
})
]);
Then you can run your collection through and filter out the ones that aren't valid like in #pseudoanime's answer.

Laravel 5.2 - Insert n number rows to database with one submit request & custom validation message with row number

I am developing a simple task management web application using laravel. The requirement states that we need to save the general information such as TaskDate, AssignedTo in a taskinfo table. List of tasks for one specific person are saved in another table called tasks. The tasks table has TaskDetailID (PK), TaskID (FK from the above table), TaskDescription, HoursRequired, etc...
The form allows users to add as many rows as they can which means a person could get assigned unlimited amount of tasks.
My problem now is saving the tasks data in the table. I've successfully saved the data for the taskinfo table, and i can even save the data for the table but only when it's one column.
Here is my store function on TaskInfoController
public function store(Request $request)
{
$validator = Validator::make(
$request->all(),
[
'TaskDate.*' => 'required',
'AssignedTo.*' => 'required',
]
,
[
'TaskDate.*.required' => 'Task Date is required.',
'AssignedTo.*.required' => 'Please assign the task to someone.',
]
);
if ($validator->fails())
{
//redirect errors to mars
}
$taskinfo = new TaskInfo();
$taskinfo->TaskDate = Carbon::createFromFormat("m/d/Y", $request->input('TaskDate'));
$taskinfo->TaskAssignedTo = $request->input('TaskAssignedTo');
// Some more columns here
$taskinfo->Save();
// Now for the tasks table
$tasksbulkinsert = array();
foreach ($request->input('TaskDescription') as $taskdescription)
{
$tasksbulkinsert[] = array('TaskID' => Uuid::uuid4(), 'TaskDescription' => $taskdescription);
}
Task::insert($tasksbulkinsert);
return redirect()->action('TaskInfoController#index')->with('flash_message', 'Successfully Saved!');}
The above code actually works perfectly but i don't know how i can insert the HoursRequired, or any additonal value with the corresponding taskdescription on the tasks table.
I tried a few approaches
having an incremental count such as i++ to know which row index (so to speak) of the taskdescription the query is currently procession, and having another foreach loop with it's own counter for the hoursrequired input and getting the value where the taskdescription's counters is equal to the hoursrequired counter. But it didn't work and even if it did, i don't think having multiple foreach loops for every column is good for performance.
Having different arrays with their own foreach loop to get the values from the inputs and then somehow merge the arrays.
Here is my HTML form
<input class="form-control" name="TaskDescription[]" type="text">
<input class="form-control" name="HoursRequired[]" type="text">
Main Question.
How can I save the TaskDescription and the HoursRequired into the database with one query.
Not so important question
The array validation at the top works but is there a way to have an error message that states the row of the error.
For example, Date is required for row number n.
You can simply:
foreach ($request->input('TaskDescription') as $i=>$taskdescription)
{
$tasksbulkinsert[] = array(
'TaskID' => Uuid::uuid4(),
'TaskDescription' => $taskdescription,
'HoursRequired' => $request->input('HoursRequired')[$i]
);
}
For your second question:
$messages = [];
foreach($this->request->get('TaskDescription') as $key => $val)
{
$messages['TaskDescription.'.$key.'.required'] = 'TD is required for row $key';
$messages['some_field.'.$key.'.some_rule'] = 'some_field custom message on row $key';
}
$validator = Validator::make(
$request->all(),
[
'TaskDate.*' => 'required',
'AssignedTo.*' => 'required',
]
,
$messages;

How to filter a result returned by a function get_entries() of a stream entries driver in pyrocms?

I have a stream/table named profiles. All of its column are stream-fields. I am trying to restrict the result returned by the the function, get_entries() depending on some criteria. Below is my code:
$data = [
'stream' => 'profiles',
'namespace' => 'users',
'where' => 'user_id = 3' // lets say, this is my criteria
];
$row = $this->streams->entries->get_entries($data); // returns empty
The varaible, $row resulted in empty array. Although there is one row in table, profiles where user_id is 3. I have read the documentation of pyrocms and it pretty much says the exact way to use the where clause (just like above).
NOTE: I have also tried writing like
'where' => 'profiles.user_id = 3'`
joy !to avoid table conflict. Still no
But when I write the code like this:
$row = $this->streams->entries->get_entries($query);
$query = [
'stream' => 'profiles',
'namespace' => 'users'
];
// No where clause this time
$row = $this->streams->entries->get_entries($query);
This time $row returns all rows including the row with user id 3.
I am unable to use the where clause in get_entries in a right way. I might have done some mistake. Help me out guyz
NOTE: I am using community edition.
I think this might be due to a bug (well, not a bug, but a feature that doesn't work as intended).
If I'm intentionally issue a wrong query, the sql query output is
SELECT [ ... ] LEFT JOIN `default_profiles` as `profiles` ON `profiles`.`user_id`=`default_profiles`.`created_by` WHERE (user_id` = 1) ORDER BY `default_profiles`.`created` DESC
Here you see that PyroCMS tries to lookup the data for the "created_by" field. And that doesn't work in this case.
If you disable the 'created_by' field, you should get the correct row:
$this->streams->entries->get_entries(
array(
'stream' => 'profiles',
'namespace' => 'users',
'where' => 'user_id = 3',
'disable' => 'created_by'
)
);
It would be great if you could file an issue on the pyrocms github page. If you won't I'll do it in the next few days.
Model
public function get_entries($table, $where) {
$this->db->select('*');
$this->db->from($table);
foreach ($where as $key => $value) {
$this->db->where($key, $value);
}
$this->query = $this->db->get();
foreach ($this->query->result_array() as $row) {
$array1[] = $row;
}
if ($this->query->num_rows() == 0)
return false;
else
return $array1;
}
call this model function as
$row = $this->streams->entries->get_entries('profiles',array('user_id '=>3));

Categories