Yii2 array elements to display in different rows in detail view - arrays

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,
]);

Related

Cakephp retrieving row data from a table

I'm attempting to insert data from a row in one table into another following a button click. I have a table containing class information. When the user clicks register, I need to take the id of that class and insert it into my timetable table.
So far I'm trying to obtain the id from the POST data. However, when I run the debugger, the id for the class in the array is empty.
My register function:
public function register(){
$classestimetable = $this->Classestimetable->newEntity();
if ($this->request->is('post')) {
$classestimetable->user_id = $this->Auth->user('id');
$classestimetable->unit_id = $this->request->getData(['id']);
debug($classestimetable);exit;
if ($this->Classestimetable->save($classestimetable)) {
$this->Flash->success(__('The class has been added to your schedule.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The class could not be added. Please, try again.'));
}
}
Table code:
<tbody>
<?php foreach ($units as $unit): ?>
<tr>
<td hidden><?= h($unit->id) ?></td>
<td><?= h($unit->className) ?></td>
<td><?= h($unit->user->firstName), ' ', h($unit->user->lastName) ?></td>
<td><?= h($unit->classDate) ?></td>
<td><?= h($unit->classTime) ?></td>
<td class="actions">
<?= $this->Form->postLink(__('Register'), ['action' => 'register', $unit->id], ['confirm' => __('Are you sure you want to register for "{0}"?', $unit->className)]) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
Debug output:
object(App\Model\Entity\Classestimetable) {
'user_id' => (int) 11,
'unit_id' => null,
'[new]' => true,
'[accessible]' => [
'unit_id' => true,
'user_id' => true,
'unit' => true,
'user' => true
],
'[dirty]' => [
'user_id' => true,
'unit_id' => true
],
'[original]' => [],
'[virtual]' => [],
'[hasErrors]' => false,
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Classestimetable'
}
When working I'm hoping the unit_id field will contain the id of the class the user has chosen to register for.
You are using $this->request->getData(['id']) to get the id. But in the URL you're building, you haven't named that parameter. Either change to:
['action' => 'register', 'id' => $unit->id]
or else change the definition of your function to:
public function register($id)
and then use $classestimetable->unit_id = $id;

Duplicate entry with cakePhp

I am trying to apply data by adding _copy to the name and of course a new ID but I am not at all expert cakePHP and I do not know where to start. Here is my view:
I add the button "duplicate" with a route in the controller
<td>
<?php echo $this->Html->link(
'Dupliquer',
array(
'controller' => 'contracts',
'action' => 'duplicate',
$contract['Contract']['id']
),
array(
'class' => 'btn btn-default btn-sm'
)
); ?>
</td>
The function I call in my controller:
public function duplicate($id = null) {
if (!$id)
{
throw new NotFoundException(__('Identifiant invalide'));
}
$contract = $this->Contract->find('first', [
'conditions' => [
'Contract.id' => $id
]
]);
if (!$this->Contract->HasAny(['Contract.id' => $id])) {
throw new NotFoundException(__('Le contrat n\'a pas pu ĂȘtre trouvĂ©'));
}
$data = [
'name' => $contract['Contract']['name']. '_copy',
];
return $this->redirect('edit');
}
In my function I retrieve the information to duplicate and it is here that I stuck at the level of the recording. Do you have an idea how to do it to make it clean?
Thank you.
Change:
$contract = $this->Contract->findById($id);
to:
$contract = $this->Contract->find()->where(['id' => $id])->first();
then:
$data = [
'name' => $contract->name. '_copy',
// other fields ...
];
$entity = $this->Contracts->newEntity($data);
$this->Contracts->save($entity);
// ...
// $this->redirect($this->referer());

Cakephp : display data in a table with other table's data

I'm displaying my users in a table, and I am retrieving the company name and city with company_id
users :
id lastname firstname company_id role
1 doe john 2 author
2 deo jenne 2 admin
3 deus Joe 1 admin
4 Stewart Rob 1 author
companies :
id name city
1 New-York Company New-York
2 Paris Company Paris
user display :
<?php
foreach($users as $user):?>
<tr>
<td><?= $user->lastname?></td>
<td><?= $user->firstname?></td>
<td><?= $user->companies->name ?></td>
<td><?= $user->companies->city?></td>
<td><?= $user->role?></td>
</tr>
<?php endforeach;?>
usersTable.php :
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class UsersTable extends Table
{
public function initialize(array $config){
$this->belongsToMany('Companies');
$this->belongsTo('Companies', [
'foreignKey' => 'company_id',
]);
}
}
?>
So in my company display, I would like to retrieve admin and author names, like so :
<?php
foreach($companies as $company):?>
<tr>
<td><?= $company->name?></td>
<td><?= $company->city?></td>
<td><?= $company->users-> *adminlastname* ?></td>
<td><?= $company->users-> *adminfirstname* ?></td>
<td><?= $company->users-> *authorlastname* ?></td>
<td><?= $company->users-> *authorfirstname* ?></td>
</tr>
<?php endforeach;?>
I have tried getting company id to reference company_id in users for starters to get on track, without success(trying to get property of non-object)
Furthermore, would it work if I have several authors and admins ? Less likely to several admins per companies, but can happen, and several authors is definetely possible
companiesTable :
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class companiesTable extends Table
{
public function initialize(array $config){
//$this->addBehavior('Timestamp');
$this->hasMany('Users');
$this->hasMany('companies', [
'foreignKey' => 'identite',
]);
$this->hasMany('Admins', [
'className' => 'Users',
'conditions' => [
'Admins.role' => 'admin'
]
]);
$this->hasMany('Authors', [
'className' => 'Users',
'conditions' => [
'Admins.role' => 'author'
]
]);
}
public function isOwnedBy($companyId, $userId){
$company = $this
->find('all',['conditions' => ['companies.id'=>$companyId]])
->matching('Users', function ($q) use ($userId) {
return $q->where(['Users.id' => $userId]);
})
->first();
if ($company) return true;
return false;
}
}
Edit : adding Companies/index.ctp, still too new, I do not see at all what's wrong... :
<div class="row">
<div class="medium-4 small-12 columns">
<div style="padding: 22px;">
<h2>companies</h2>
</div>
<div id="customAdd" style="clear:both;padding: 22px;">
add
<div style="clear:both;height:0px;"></div>
</div>
<div id="customSearch" style="clear: both;padding: 22px">
<p style="font-size:36px; display: inline-block;">search for a company</p><input type="text" id="searchBox">
</div>
</div>
<div class="medium-8 small-12 columns">
<div>
<table id="companies" class="cell-border dataTable no-footer">
<thead>
<tr>
<th>name</th>
<th>city</th>
<th>last name</th>
<th>first name</th>
</tr>
</thead>
<tbody>
<?php foreach ($company->users as $user):?>
<tr class='clickable-row' data-href="/companies/edit/<?= $company->id?>">
<td><?= $company->name?></td>
<td><?= $company->city?></td>
<td><?= $company->users[0]->lastname?></td>
<td><?= $company->users[0]->firstname?></td>
</tr>
<?php endforeach;?>
</tbody>
</table>
</div>
</div>
</div>
I am getting these errors :
Notice (8): Undefined variable: company [APP/Template/Companies/index.ctp, line 36]
Notice (8): Trying to get property of non-object [APP/Template/Companies/index.ctp, line 36]
Warning (2): Invalid argument supplied for foreach() [APP/Template/Companies/index.ctp, line 36]
which corresponds to this :
<?php foreach ($company->users as $user):?>
CompaniesController :
<?php
namespace App\Controller;
use Cake\ORM\TableRegistry;
class CompaniesController extends AppController{
public function index(){
$companies = $this->Companies
->find('all',['order' => ['Companies.name' => 'ASC']]);
$this->set(compact('companies'));
$this->loadModel('Users');
$users = $this->Users->find('all')
->contain(['companies']);
$this->set(compact('users'));
}
public function view($id = null){
$company = $this->Companies->get($id);
$this->set(compact('company'));
}
public function add(){
$company = $this->Companies->newEntity();
if ($this->request->is('post')) {
if ($this->Companies->save($company)) {
$this->Flash->success(__('Your company has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Unable to add your company.'));
}
$this->set('company', $company);
$this->set(compact('companies'));
}
public function edit($id = null){
$company = $this->Companies->get($id);
if ($this->request->is(['post', 'put'])) {
$company = $this->Companies->patchEntity($company, $this->request->data,['associated' => ['Users']]);
if ($this->Companies->save($company)) {
$this->Flash->success(__('Your company has been updated.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('Unable to update your company.'));
}
$this->set('company', $company);
$this->set(compact('companies'));
}
public function delete($id){
$this->request->allowMethod(['post', 'delete']);
$company = $this->Companies->get($id);
if ($this->Companies->delete($company)) {
$this->Flash->success(__('The company with id: {0} has been deleted.', h($id)));
return $this->redirect(['action' => 'index']);
}
}
public function isAuthorized($user){
if ($this->request->action === 'logout') {
return true;
}
if ($this->request->action === 'settings') {
return true;
}
return parent::isAuthorized($user);
}
}
thanks in advance
First things first, you cannot define multiple associations with the same name. Given that you have a company_id field, that is a belongsTo association from the users point of view.
Given these prerequisites, it's naturally a hasMany association from the companies point of view. I assume that you've set that up already in your CompaniesTable class, if not, then that's what you need to do first.
The users property will then be an array of User entitiy objects, so you need to access it accordingly, that is either directly via an index:
$company->users[0]->firstname
or by iterating over it:
foreach ($company->users as $user) {
// $user->firstname
}
If you need to handle the users separated by their roles, then you could for example filter them accordingly:
$admins = collection($company->users)->filter(function ($user) {
return $user->role === 'admin';
});
or maybe better yet create separate associations using conditions:
$this->hasMany('Admins', [
'className' => 'Users',
'conditions' => [
'Admins.role' => 'admin'
]
]);
$this->hasMany('Authors', [
'className' => 'Users',
'conditions' => [
'Admins.role' => 'author'
]
]);
That way you could contain and access the different types of users separately:
$company->admins
$company->authors
See also
Cookbook > Database Access & ORM > Associations - Linking Tables Together
Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Eager Loading Associations Via Contain

CakePHP 3.4 group by foreach loop the result

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()

cant edit data after using a find but needed a get

I cant edit data from a table of rows from a find command as I get an unknown method 'isNew'. I understand I didnt use a get command to retrieve the data but I used a find instead. The data returned is correct (array given below) but it is in the wrong format . I used a query and need to convert to an entity but I dont know how to do this.
My question is how do I get data with a get command with where conditions if I need to use get for edit functions to make this work?
https://github.com/cakephp/cakephp/issues/5262
Argument 1 passed to Cake\ORM\Table::patchEntity() must be an instance of Cake\Datasource\EntityInterface, instance of Cake\ORM\Query given
public function edit2($id = null)
{
$a3 = '2016-05-28';
$a4 = '2016-06-01';
$lessons = $this->find()
->contain(['Tutors', 'Subjects', 'TutoringTypes', 'Terms', 'Students'])
->select([
'Lessons.id', 'Lessons.lesson_date', 'Lessons.lesson_plan',
'Tutors.id', 'Tutors.first_name', 'Tutors.last_name',
'Subjects.name', 'TutoringTypes.value'
])
->where([
'Lessons.lesson_date >' => $a3,
'Lessons.lesson_date <' => $a4,
'Lessons.tutor_id' => 12
])
->order(['Lessons.lesson_date' => 'ASC'])
->hydrate(true);
$lessons->matching('Students', function ($q) use ($a8) {
return $q
->select(['Students.id', 'Students.first_name', 'Students.last_name'])
->where(['Students.first_name like' => '%' . $a8 . '%']);
});
if ($this->request->is(['patch', 'post', 'put'])) {
debug($this->request->data);
$lessons = $this->Lessons->patchEntity($lessons, $this->request->data);
if ($this->Lessons->save($lesson)) {
$this->Flash->success(__('The lesson has been saved.'));
return $this->redirect(['action' => 'edit2']);
} else {
$this->Flash->error(__('The lesson could not be saved. Please, try again.'));
}
}
$this->set(compact('lessons'));
$this->set('_serialize', ['lessons']);
}
view
<?php foreach ($lessons as $key => $item):
//foreach ($query3 as $item):
?>
<tr>
<?php echo $this->Form->hidden($key . '.id', ['label' => '', 'value' => $item->id]) ?>
<td><?= $item->id ?></td>
<td><?= $item->lesson_date ?></td>
<td><?= $item->tutor->first_name . ' ' . $item->tutor->last_name ?></td>
<td><?= $item->students[0]->first_name . ' ' . $item->students[0]->last_name ?></td>
<td><?= $item->subject->name ?></td>
<td><?= $item->tutoring_type->value ?></td>
<td><?php echo $this->Form->input($key . '.lesson_plan', [
'label' => '',
'value' => $item->lesson_plan,
'style' => 'width: 300px; height:50px;'
]) ?></td>
</tr>
<?php endforeach; ?>
returned data
[
(int) 0 => [
'id' => '12397',
'lesson_plan' => 'hello'
],
(int) 1 => [
'id' => '12408',
'lesson_plan' => ''
],
(int) 2 => [
'id' => '14432',
'lesson_plan' => ''
],
(int) 3 => [
'id' => '12419',
'lesson_plan' => ''
],
(int) 4 => [
'id' => '12441',
'lesson_plan' => ''
]
]
The reason for the error is $lessons is a query returning 5 objects. It's illogical to call save on it as if it were one lesson.
Save one entity at a time
If the submitted data is the structure shown in the question, code similar to this will do what you expect:
if ($this->request->is(['patch', 'post', 'put'])) {
$patched = $this->Lessons->patchEntities($lessons, $this->request->data());
foreach ($patched as $entity) {
$this->Lessons->save($entity);
}
}
Note that how to patch multiple entries is covered in the docs

Resources