CakePHP 3.x dynamic finder method - cakephp-3.0

I have this problem with a CakePHP 3 application
I am trying to get a category by it's slug and all related articles belonging to that category.
I am using dynamic finder method findBySlug in the Controller but it throws an error in the view.
Here is my code:
public function view($slug = null)
{
if (!$slug) {
throw new NotFoundException(__('Invalid category slug'));
}
$category = $this->Categories->findBySlug($slug, [
'contain' => [
'Articles'
]
]);
$this->set(compact('category'));
}
and the view:
<div class="categories view">
<h2><?= h($category->name); ?></h2>
<?php foreach ($category->articles as $article): ?>
<?php echo $article->title; ?>
<?php endforeach; ?>
Can anyone please provide or point me to a solution ?
Thank you in advance
And this is the debug I am getting in the controller:
object(App\Model\Entity\Category) {
'new' => false,
'accessible' => [
'name' => true,
'slug' => true,
'articles' => true
],
'properties' => [
'id' => (int) 2,
'name' => 'International',
'slug' => 'international.html'
],
'dirty' => [],
'original' => [],
'virtual' => [],
'errors' => [],
'repository' => 'Categories'
}
and here are my models:
class CategoriesTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
$this->displayField('name');
$this->hasMany('Articles', [
'className' => 'Articles',
'foreignKey' => 'category_id',
'conditions' => [
'published' => 1
],
'dependent' => true
]);
}
}
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
$this->belongsTo('Users');
$this->belongsTo('Categories', [
'foreignKey' => 'category_id'
]);
}
}

the find() method will always return a Query object. You need to fetch at least one result before trying to get properties from it:
$thisIsAQuery = $this->Categories->findBySlug($slug)->contain(['Articles'])
// I can now fetch the category
$category = $thisIsAQuery->first();
// And now I can get the category name
echo $category->name

Related

Yii2 Select2 multiple with ajax: Illegal offset type

I use Yii2 and widget Select2. I want onkeyup to search products from table "Products" with options for multiple select, because i must save result in second table "rel_products". I do know why it return error : "Illegal offset type"
Here is model:
public $products = array(); =>because i write result in second table
here is view:
$url = \yii\helpers\Url::to(['/product/prodlist']);
echo $form->field($model, 'products')->widget(Select2::classname(), [
'initValueText' => 'Search for a city ...', // set the initial display text
'model' => $model,
'attribute' => 'products',
'theme' => 'bootstrap',
'options' => ['placeholder' => 'Search for a city ...'],
'pluginOptions' => [
'allowClear' => true,
'minimumInputLength' => 3,
'ajax' => [
'url' => $url,
'dataType' => 'json',
'data' => new JsExpression('function(params) { return {q:params.term}; }')
],
'escapeMarkup' => new JsExpression('function (markup) { return markup; }'),
'templateResult' => new JsExpression('function(product) { return product.text; }'),
'templateSelection' => new JsExpression('function (product) { return product.text; }'),
],
]);
Here is Controller:
public function actionProdlist($q = null, $id = null) {
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$out = ['results' => ['id' => '', 'text' => '']];
if (!is_null($q)) {
$query = new Query;
$query->select()
->from('product')
->joinWith('translation')
->where(['like', 'title', $q])
->limit(20);
$command = $query->createCommand();
$data = $command->queryAll();
$out['results'] = array_values($data);
}
elseif ($id > 0) {
$out['results'] = ['id' => $id, 'text' => Product::find($id)->title];
}
return $out;
}
change in model:
public $products; =>because i write result in second table

Relation between belongsTo and hasMany in cakephp 3.4.9

I need to save user and invoice data at the same time. When I am submitting the form, the user data is saving, but the invoice table isn't. When I print the request data, I get the following:
object(Cake\ORM\Entity) {
'u_firstname' => 'John',
'u_lastname' => 'den',
'u_email' => 'john#man.com',
'u_phone' => '123',
'password' => '123',
'membership_id' => (int) 1,
'Invoices' => [
'inv_membership_name' => 'basic1',
'inv_membership_cost' => '55',
'inv_purchase_date' => '2017-07-11'
],
'id' => (int) 19,
'[new]' => false,
'[accessible]' => [
'*' => true
],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Users'
}
I am not able to save the Invoice part. I define InvoicesTable.php
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('invoices');
$this->setDisplayField('id');
$this->setPrimaryKey('id');
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
}
and UsersTable like this:
$this->hasMany('Invoices', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
I use the following code to save data:
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->data(), [
'associated' => [
'Invoices'
]
]);
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
}else{
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
}
Please suggest what I'm missing. Or how I should debug.
When you print request data invoices array should be something like this:
object(Cake\ORM\Entity) {
'invoices' => [
[0] => Array
(
['inv_membership_name'] => 'basic1',
['inv_membership_cost'] => '55',
['inv_purchase_date'] => '2017-07-11'
),
[1] => array('...') // if you have multiple records
],
For that change input name to something like this in your form:
invoices[]['inv_membership_name']
invoices[]['inv_membership_cost']
invoices[]['inv_purchase_date']
See here Saving HasMany Associations in CakePhp3.
You are close to the solution but you have the naming conventions wrong in the form. The correct name would be invoices.0.inv_membership_name
invoices.1.inv_membership_name etc. I included the creating inputs for associated data link from the cookbook.
echo $this->Form->control('invoices.0.inv_membership_name');
https://book.cakephp.org/3.0/en/views/helpers/form.html#creating-inputs-for-associated-data

Login with other fields using Auth in cakephp3

Problem regarding login with Auth in cakephp3
$this->loadComponent('Auth', [
'loginRedirect' => [
'controller' => 'Articles',
'action' => 'index'
],
'logoutRedirect' => [
'controller' => 'Pages',
'action' => 'display',
'home'
]
]);
it only allows me to use username by default, Okay If I wanted to login using email, that through I have searched and got this:
UserController.php
public function login()
{
if ($this->request->is('post'))
{
$this->Auth->config('authenticate', [
'Form' => [
'fields' => ['username' => 'email']
]
]);
$this->Auth->constructAuthenticate();
$this->request->data['email'] = $this->request->data['username'];
unset($this->request->data['username']);
$user = $this->Auth->identify();
if ($user)
{
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}
I can change fields from username to email and reconstruct it but What if I wanted to login with ID field.
$this->Auth->config('authenticate', [
'Form' => [
'fields' => ['username' => 'id']
]
]);
If I change from email to id, it is not allowing me to login. Do I have to use then queries instead?
I just had the same issue and solved it like this using cakePHP 3.3
Your AppController
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'email',
]
]
],
'loginAction' => [
'controller' => 'Users',
'action' => 'login'
],
'unauthorizedRedirect' => $this->referer(),
'loginRedirect' => [
'plugin' => false,
'controller' => 'Pages',
'action' => 'home',
],
]);
Your UserController (login action)
/**
* #return Response|null
*/
public function login()
{
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error('Your username or password is incorrect.');
}
}
Your basic Users/login.ctp template
<?= $this->Form->create() ?>
<?= $this->Form->input('email') ?>
<?= $this->Form->input('password') ?>
<?= $this->Form->button('Login') ?>
<?= $this->Form->end() ?>
Your problem is, when you changed the 'email' field to 'id' in the below code,
`('authenticate', ['Form' => ['fields' => ['username' => 'id']]]);`
you should have changed the input field of your view 'login.ctp'.
from this: <?= $this->Form->input('email') ?>
to this: <?= $this->Form->input('id') ?>

CakePHP 3 - relationship between models

I related two models following the official documentation of CakePHP 3 and can not return the values of one of them in view (Template).
The Code:
Work - Entity
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Work extends Entity
{
protected $_accessible = [
'project' => true,
'client' => true,
'filter' => true,
'tech_1' => true,
'tech_2' => true,
'tech_3' => true,
'tech_4' => true,
'job' => true,
'status' => true,
'link' => true,
];
}
WorksImage - Entity
namespace App\Model\Entity;
use Cake\ORM\Entity;
class WorksImage extends Entity
{
protected $_accessible = [
'photo' => true,
'photo_dir' => true,
'work_id' => true,
'work' => true,
];
}
PagesController - Controller:
namespace App\Controller;
use Cake\Core\Configure;
use Cake\Network\Exception\NotFoundException;
use Cake\View\Exception\MissingTemplateException;
class PagesController extends AppController
{
public function portfolio()
{
$this->loadModel('Works');
$this->loadModel('WorksImages');
$works = $this->Works->find('all',['contain' => ['WorksImages'],'limit' => 10, 'order' => ['Works.created' => 'DESC']]);
$this->set(compact('works'));
}
}
WorksTable - Table:
namespace App\Model\Table;
use App\Model\Entity\Work;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class WorksTable extends Table
{
public function initialize(array $config)
{
$this->table('works');
$this->displayField('project');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->hasOne('WorksImages', [
'foreignKey' => 'work_id'
]);
}
WorksImagesTable - Table
namespace App\Model\Table;
use App\Model\Entity\WorksImage;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class WorksImagesTable extends Table
{
public function initialize(array $config)
{
$this->table('works_images');
$this->displayField('id');
$this->primaryKey('id');
$this->addBehavior('Timestamp');
$this->belongsTo('Works', [
'foreignKey' => 'work_id',
'joinType' => 'INNER'
]);
}
Portfolio - View (Template)
<div class="container">
<div class="span12">
<h1>Portfólio</h1>
<div>
<?php foreach ($works as $work): ?>
<div>
<p><?= 'Conteúdo da tabela Works = ' . $work->project ?></p>
<p><?= 'Conteúdo da tabela WorksImages = ' . $work->work_id ?></p>
</div>
<?php endforeach ?>
</div>
</div>
</div>
I can not return any value from the WorksImagesTable model. During Debugging, I realize that the tables are related, in addition, cake returns no error in the view.
I can not understand what is wrong.
I thank in advance any help.
Thanks.
In the foreach loop (debug($work->works_image)) returned something like this:
object(App\Model\Entity\WorksImage) {
'new' => false,
'accessible' => [
'photo' => true,
'photo_dir' => true,
'work_id' => true,
'work' => true
],
'properties' => [
'id' => (int) 1,
'photo' => 'arteviva.jpg',
'photo_dir' => 'a4cd522c-b7b9-437a-99fc-0eb15827944f',
'work_id' => (int) 1,
'created' => object(Cake\I18n\Time) {
'time' => '2015-05-08T20:25:07+0000',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'modified' => null
],
'dirty' => [],
'original' => [],
'virtual' => [],
'errors' => [],
'repository' => 'WorksImages'
}

CakePHP 3 hasMany will not pass parent ID to associated children

/* ShootsTable.php Meta Table */
public function initialize(array $config)
{
$this->table('shoots');
$this->displayField('title');
$this->primaryKey('id');
$this->hasMany('ShootMeta');
}
/* ShootMetaTable.php Meta Table */
public function initialize(array $config)
{
$this->table('shoot_meta');
$this->displayField('id');
$this->primaryKey('id');
$this->belongsTo('Shoots');
}
public function buildRules(RulesChecker $rules)
{
$rules->add($rules->existsIn(['shoots_id'], 'Shoots'));
return $rules;
}
/* Shoots.php Controller */
public function add()
{
$shoot = $this->Shoots->newEntity(null);
if ($this->request->is('post')) {
$this->Shoots->patchEntity($shoot, $this->request->data,[
'associated' => ['ShootMeta']
]);
$shoot->set('created_by', 1);
debug($shoot);
if ($this->Shoots->save($shoot,['associated' => ['ShootMeta']])) {
$this->Flash->success('The shoot has been saved.');
// return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error('The shoot could not be saved. Please, try again.');
}
}
$this->set(compact('shoot'));
$this->set('_serialize', ['shoot']);
}
/* Add.ctp Template */
<div class="shoots form large-10 medium-9 columns">
<?= $this->Form->create($shoot); ?>
<fieldset>
<legend><?= __('Add Shoot') ?></legend>
<?php
echo $this->Form->input('title');
echo $this->Form->input('content');
echo $this->Form->input('datetime', ['label' => 'Date/Time Of Shoot']);
echo $this->Form->input('shoot_meta.0.meta_key', ['type' => 'hidden', 'value' => 'photographer_spaces']);
echo $this->Form->input('shoot_meta.0.meta_value',['label' => 'Photographer Spaces', 'type' => 'number']);
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
/* debug($shoots) output */
object(App\Model\Entity\Shoot) {
'new' => true,
'accessible' => [
'created_by' => true,
'title' => true,
'content' => true,
'datetime' => true,
'shoot_meta' => true
],
'properties' => [
'title' => '123',
'content' => '123',
'datetime' => object(Cake\I18n\Time) {
'time' => '2015-03-19T07:04:00+0000',
'timezone' => 'UTC',
'fixedNowTime' => false
},
'shoot_meta' => [
(int) 0 => object(App\Model\Entity\ShootMetum) {
'new' => true,
'accessible' => [
'shoots_id' => true,
'meta_key' => true,
'meta_value' => true,
'shoot' => true
],
'properties' => [
'meta_key' => 'photographer_spaces',
'meta_value' => '123'
],
'dirty' => [
'meta_key' => true,
'meta_value' => true
],
'original' => [],
'virtual' => [],
'errors' => [
'shoots_id' => [
'_required' => 'This field is required'
]
],
'repository' => 'ShootMeta'
}
],
'created_by' => (int) 1
],
'dirty' => [
'title' => true,
'content' => true,
'datetime' => true,
'shoot_meta' => true,
'created_by' => true
],
'original' => [],
'virtual' => [],
'errors' => [],
'repository' => 'Shoots'
}
As you can see, the field shoots_id is required, which I would have thought would be automatically passed down (although at this point it hasn't executed any MySQL).
I feel I may have gone about this the wrong way but have spent 2 full days trying to get it right. One of those days was me trying to work out why after baking it had named a lot of the references to ShootMeta to ShootMetum, I thought it had actually corrupted it.
One of the biggest issues I have is knowing where to use shoot_meta, ShootMeta, shootmeta, shootmetum, ShootMetum etc. It feels like a bit of a minefield!
/Update
A dump of the save object below. It is clearly assigning it, it just seems to not be executing it in the SQL?
'shoot_meta' => [
(int) 0 => object(App\Model\Entity\ShootMetum) {
'new' => false,
'accessible' => [
'shoots_id' => true,
'meta_key' => true,
'meta_value' => true
],
'properties' => [
'meta_key' => 'photographer_spaces',
'meta_value' => '123',
'shoot_id' => '2',
'id' => '3'
],
'dirty' => [],
'original' => [],
'virtual' => [],
'errors' => [],
'repository' => 'ShootMeta'
},
Found it.
It is referring to shoot_id when i debug the save
'shoot_meta' => [
(int) 0 => object(App\Model\Entity\ShootMetum) {
'new' => false,
'accessible' => [
'shoots_id' => true,
'meta_key' => true,
'meta_value' => true
],
'properties' => [
'meta_key' => 'photographer_spaces',
'meta_value' => '123',
'shoot_id' => '2',
'id' => '3'
],
'dirty' => [],
'original' => [],
'virtual' => [],
'errors' => [],
'repository' => 'ShootMeta'
},
for some reason it was using the singular name for the association. Changed in the Shoots.php model.
From
$this->hasMany('ShootMeta');
To
$this->hasMany('ShootMeta',[
'foreignKey' => 'shoots_id'
]);
Remove the validation rule for shoots_id. Validation is for data that is posted from the form, and in this case the foreignKey cannot be posted from the Form. You already have rules in your buildRules() method for making sure that value is passed before saving, so removing the validation is 100% safe.
i have same problem like this to, for now my solution is sending associated data to other function/methode and save it.
eg
**
public function add() {
$kantor = $this->Kantor->newEntity($this->request->data);
if ($this->request->is('post')) {
$kantor = $this->Kantor->patchEntity($kantor, $this->request->data);
$rgndata = $this->request->data['Telpkantor'];
$this->request->session()->write('rgndata', $rgndata);
if ($this->Kantor->save($kantor)) {
$result = $this->Kantor->save($kantor);
$this->addTelpKantor($rgndata, $result->id);
$this->Flash->success('The kantor has been saved.');
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error('The kantor could not be saved. Please, try again.');
}
}
$reffKota = $this->Kantor->ReffKota->find('list', ['limit' => 200]);
$statusKantor = $this->Kantor->StatusKantor->find('list', ['limit' => 200]);
$pimpinan = $this->Kantor->Pimpinan->find('list', ['limit' => 200]);
$jenisTelp = $this->Kantor->Telpkantor->Jenistelp->find('list', ['limit' => 200]);
$this->set(compact('kantor', 'reffKota', 'statusKantor', 'pimpinan', 'jenisTelp'));
$this->set('_serialize', ['kantor']);
}
public function addTelpKantor($rgndata = null, $kantor_id=null) {
if (!empty($rgndata[0]['noTelp'])) {
$this->loadModel('Telpkantor');
foreach ($rgndata as $rgndata) {
$rgndata['kantor_id'] =$kantor_id;
$rgndatasave = $this->Telpkantor->newEntity($rgndata);
$this->Telpkantor->save($rgndatasave);
}
}
}
**