I am struggling with foreaching grouped records.
It is a collection of hours saved with the same date, and I want to group them in my view/output.
Any suggestions? I have tried a ton of different approaches. Here is how I get them from DB. (date = type date in mysql "Y-m-d"). And outputting them normal. All my trials and errors.. I could write all day, and I am sure it is a simple solution for someone that know how cake libs work.
$hours = $this->UserHours->find('all', [
'conditions' => [
'UserHours.user_id' => $this->Auth->user('id'),
'month(UserHours.date)' => $this->request->data('date.month')
],
'order' => ['UserHours.date ASC'],
'group' => ['UserHours.date']
]);
And the foreach loop:
foreach ($projecthours as $hour):
debug($hour);
?>
<tr>
<td> <?php echo $hour->date;?> </td>
<td> <?php echo $hour->start; ?> </td>
<td> <?php echo $hour->end; ?> </td>
</tr>
My problem is that I have no idea how to loop trough the records correctly when grouped from controller. I cant find them when using the "group" condition in controller. I only get the first record of each date when using "group".
:) I guess they are somewhere inside there, but I cant figure this one out. I am quite new to cake 3.4..
After you get the grouped dates, you get all dates matching each grouped date.
// get grouped dates
$dates = $this->UserHours->find('all', [
'conditions' => [
'UserHours.user_id' => $this->Auth->user('id'),
'month(UserHours.date)' => $this->request->data('date.month')
],
'order' => ['UserHours.date ASC'],
'group' => ['UserHours.date']
]);
$hours = [];
// find all dates for each grouped date
foreach ($dates as $date) {
$hours[] = $this->UserHours->find('all', [
'conditions' => [
'UserHours.user_id' => $this->Auth->user('id'),
'month(UserHours.date)' => $this->request->data('date.month')
],
'order' => ['UserHours.date ASC']
])
->where(['UserHours.date' => $date->date]);
}
foreach ($hours as $values) {
foreach ($values as $hour) {
dd($hour->date);
}
}
That's the expected result, as that is how SQL grouping works, you get one value per group, there simply will be no further values in the results.
If you just want to output things in a grouped manner, then that's something different, for that you have to select all data, and then you could simply group the results on PHP level, for example using the collection/resultset method groupBy():
$query = $this->UserHours->find('all', [
'conditions' => [
'UserHours.user_id' => $this->Auth->user('id'),
'month(UserHours.date)' => $this->request->data('date.month')
],
'order' => ['UserHours.date ASC']
]);
// callback usage required as `date` is most likely an object
$groupedByDate = $query->all()->groupBy(function ($row) {
return $row['date']->format('Y-m-d');
});
// if it weren't an object, then passing the fields name would already do it
// $groupedByDate = $query->all()->groupBy('date');
foreach ($groupedByDate as $date => $group)
{
// $date = the value of UserHours.date shared by all entities in the group
// $group = an array of UserHour entities that share the same UserHours.date value
foreach ($group as $hour)
{
// ...
}
}
See also
Cookbook > Database Access & ORM > Query Builder > Queries Are Collection Objects
Cookbook > Collections > Collection::groupBy()
Related
Products belongsToMany Categories and Categories hasMany Products, inside my Product view I'm showing a list of all it's categories but I want to paginate or limit these results.
My current code on ProductsController is:
$product = $this->Products
->findBySlug($slug_prod)
->contain(['Metas', 'Attachments', 'Categories'])
->first();
$this->set(compact('product'));
I know I need to set $this->paginate() to paginate something but I can't get it working to paginate the categories inside the product. I hope you guys can understand me.
UPDATE: Currently I have this going on:
$product = $this->Products->findBySlug($slug_prod)->contain([
'Metas',
'Attachments',
'Categories' => [
'sort' => ['Categories.title' => 'ASC'],
'queryBuilder' => function ($q) {
return $q->order(['Categories.title' => 'ASC'])->limit(6);
}
]
])->first();
The limit works but I don't know how to paginate yet
The paginator doesn't support paginating associations, you'll have to read the associated records manually in a separate query, and paginate that one, something along the lines of this:
$product = $this->Products
->findBySlug($slug_prod)
->contain(['Metas', 'Attachments'])
->first();
$categoriesQuery = $this->Products->Categories
->find()
->innerJoinWith('Products', function (\Cake\ORM\Query $query) use ($product) {
return $query->where([
'Products.id' => $product->id,
]);
})
->group('Categories.id');
$paginationOptions = [
'limit' => 6,
'order' => [
'Categories.title' => 'ASC'
]
];
$categories = $this->paginate($categoriesQuery, $paginationOptions);
$this->set(compact('product', 'categories'));
Then in your view template you can display your $product and separately paginate $categories as usual.
See also
Cookbook > Controllers > Components > Pagination
Cookbook > Views > Helper> Paginator
Cookbook > Database Access & ORM > Query Builder > Filtering by Associated Data
In my project I have a function which returns an array of elements. these array of elements have converted into string using implode and has been called in the detail view. Here my detail view displays all the elements in 1 single row.
I want each and every array element to be displayed in different rowsof detail view.
My function which returns array elements.
public function getHazStatement(){
$codes = array();
$pLines = GhsHazStatements::find()->where(['haz_id' => $this->getHazID()])->all();
foreach ($pLines as $pLine){
$codes[] = $pLine->haz_statement;
//var_dump($codes); exit();
}
// var_dump(); exit();
return implode(', ', $codes);
}
public function getHazCode(){
$codes = array();
$pLines = GhsHazStatements::find()->where(['haz_id' => $this->getHazID()])->all();
foreach ($pLines as $pLine){
$codes[] = $pLine->haz_code;
//var_dump($codes); exit();
}
// var_dump(); exit();
return implode(', ', $codes);
}
My view file detail view.
[
'label' => $model->getHazCode(),
'value' => $model->getHazStatement(),
'options' => ['class' => 'table table-striped table-bordered detail-view','style'=> 'width:500px'],
],
My output is :
In my output you can see that there are two elements in the same row.. I want them to be in two different rows in detail view. How can I achieve this? Any possible solution?
Thank you
In your class:
public YourClass extends ActiveRecord
{
...
public function getHazStatemets()
{
return $this->hasMany(GhsHazStatements::className(), ['haz_id' => '<haz id column name in your class>']);
}
}
In your conroller:
$this->render('<your view file name>', [
'haz_statements' => $yourClassInstance->getHazStatements()->all(),
'options' => ... // try to avoid passing CSS classes to from view controller
])
In your view:
<?php foreach($haz_statement as $statement) ?>
<tr>
<td>
<?= $statement->haz_code ?>
</td>
<td>
<?= $statement->haz_statement ?>
</td>
</tr>
<?php endforeach; ?>
I have not exactly understood your question.
If you want to have two rows and info..: (for example)
[
'label' => $model->getHazCode(),
'value' => "detail",
'options' => ['class' => 'table table-striped table-bordered detail-view','style'=> 'width:500px'],
],
[
'label' => $model->getHazStatement(),
'value' => "detail",
'options' => ['class' => 'table table-striped table-bordered detail-view','style'=> 'width:500px'],
],
But if you want a list of ...
When you want a list of rows, this is the same GridView
Anyway, I hope the following code will help you.
Please disable Show Header (see below)
$query = GhsHazStatements::find()->where(['haz_id' => $this->getHazID()]);
$dataProvider = new \yii\data\ActiveDataProvider([
'query' => $query,
]);
echo yii\grid\GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
'haz_statement',
'haz_code',
],
'showHeader' => false,
]);
I'm fairly new to CakePHP, but think I understand the basics pretty well. I'm developing a livestock management system for a client and have run into a roadblock that I can't figure out how to approach. They would like a bulk sale process to work like this:
All Available animals displayed
Mark 1-n animals for sale
Add some additional information (IE: Buyer, total weight of the lot, etc)
Create sales record and update the sold animal's records
What I'm struggling with is how to complete the second step in this process with Cake. My thought was to show them with a checkbox using the forms helper, but that doesn't seem to be working like I want it to. I'm creating a list of the available animals and passing it to the view for display in a table, adding the checkboxes for each row.
Here is a snippet of what I currently have in the view:
<div>
<table cellpadding="0" cellspacing="0" border="1" width="50%">
<?php
echo $this->Html->tableHeaders(
array(
'Row Number',
'Ear Tag',
'Name',
array('Actions' => array('width' => '20%'))
));
// Table contents.
$counter = 1;
$rows=array();
echo $this->Form->create(null,array('action' => 'sell_cattle'));
foreach ($animals as $animal) {
$row=array();
$row[] = $counter;
$row[] = $animal['eartag'];
$row[] = $animal['name'];
$actions = array();
'action' => 'edit', $animal['id']));
$actions[] = $this->Form->checkbox('sell', array(
'type' => 'checkbox',
'value' => $animal['sell'],
'label' => 'Sell',
'hiddenField' => false
));
$row[] = array(
implode(' ', $actions),
array('class' => 'actions'),
);
$rows[]=$row;
$counter = $counter + 1;
}
if (!empty($rows)) {
echo $this->Html->tableCells($rows);
}
?>
</table>
</div>
<div>;
<?php
echo $this->Form->button(__('Sell Selected Animals'));
echo $this->Form->end();
?>
</div>
I'm hoping I'm just missing something simple or perhaps I'm approaching the problem the wrong way and there is a simpler, 'automagic' way to accomplish what I'm trying to do.
Let me know if I've not provided enough information and thanks in advance for any guidance.
I am still new to PHP, and even newer to Drupal's Form API. I'm simply trying to populate a drop down select from the database. I think the issue stems from my lack of a deep understanding of how to work with multidimensional arrays. Any help is sincerely appreciated.
My code is currently:
//Query DB for Rows
$query = db_select('hp_education_years');
$query->fields('hp_education_years', array('id', 'years',));
$query->orderBy('years', 'ASC');
$results = $query->execute();
//define rows
$options = array();
foreach ($results as $result) {
$options[$result->id] = array(
$result->years,
);
}
$form['education']['year'] = array(
'#type' => 'select',
'#title' => t('Year'),
'#options' => $options,
'#states' => array(
'visible' => array(
':input[name="data_set"]' => array('value' => 'education'),
),
),
);
This returned a populated list, but displays the year's ID in bold as well as the year (2008 for example).
How can I get the dropdown just to display the year, not the year ID in bold, and then the year. It seems like $option is just a level higher than I want it to be? if that makes sense?
Many thanks in advance.
Try changing
//define rows
$options = array();
foreach ($results as $result) {
$options[$result->id] = array(
$result->years,
);
}
to
//define rows
$options = array();
foreach ($results as $result) {
$options[$result->id] = $result->years;
}
If you look at the example from Drupal's Form API, you'll see that the Options values should be just the string value and not an array.
i have a little problem with cakephp
i have DB
measurers => id, title, color...
usages => id, measurer_id, value...
and i want to do something like
$this->paginate = [
'contain' => [
'MeasurerTypes',
'Usages' => function($q) {
return $q->find('latest');
}
],
'finder' => ['my' => $this->user['id']]
];
$this->set('title',__('My measurers'));
$this->set('measurers', $this->paginate($this->Measurers));
$this->set('_serialize', ['measurers']);
this is only example code, is there to find only one latest variable and no all list for that?
Check this:
http://book.cakephp.org/2.0/en/models/additional-methods-and-properties.html#model-getinsertid
Example:
$lastItem = $this->YOURMODEL->getInsertID();
Edit:
In CakePHP 3
http://book.cakephp.org/3.0/en/orm/retrieving-data-and-resultsets.html
$result = $articles->find('all')->all();
// Get the first and/or last result.
$row = $result->first();
$row = $result->last();