Multiple References to Associated Table in Cakephp - json

After associating two tables (many weather radar frames which belong to weather radar sites) with belongsTo and hasMany, I was hoping to generate JSON with two "contains" on the same table (and different conditions) so I can list only the frames which have specific conditions. What I currently have doesn't work; it only writes the second contain to the JSON.
Radar Site Table:
...
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('radar_site');
$this->setDisplayField('radar_id');
$this->setPrimaryKey('radar_id');
$this->hasMany('FutureFrame', [
'foreignKey' => 'radar_id'
]);
$this->hasMany('RadarFrame', [
'foreignKey' => 'radar_id'
]);
}
...
Radar Frame Table:
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('radar_frame');
$this->setDisplayField('radar_id');
$this->setPrimaryKey('radar_id');
$this->belongsTo('RadarSite', [
'foreignKey' => 'radar_id'
]);
}
Controller:
public function getRadarData()
{
$radarInfo =
$this->RadarSite->find(
'all',
[
'contain' => [
'RadarFrame' =>[
'sort' => ['RadarFrame.frame_time' => 'DESC'],
'conditions' => [
'RadarFrame.frame_time >' => time() - 60*60,
'RadarFrame.file_on_server =' => 1,
'RadarFrame.radar_type =' => 'velocity'
]
],
'RadarFrame' =>[
'sort' => ['RadarFrame.frame_time' => 'DESC'],
'conditions' => [
'RadarFrame.frame_time >' => time() - 60*60,
'RadarFrame.file_on_server =' => 1,
'RadarFrame.radar_type =' => 'reflectivity'
]
]
]
]
);
$this->set('radarInfo', $radarInfo);
}
JSON output:
...,{
"radar_id": "KHGX",
"radar_name": "Houston\/Galveston, TX",
"elevation": 18,
"tower_height": 20,
"max_latitude": 31.6485,
"min_latitude": 27.2476,
"max_longitude": -92.4946,
"min_longitude": -97.6634,
"latitude": 29.4719,
"longitude": -95.0792,
"radar_frame": [
{
"radar_id": "KHGX",
"radar_type": "reflectivity",
"frame_time": 1495473662,
"is_analyzed": 1,
"average_speed": null,
"average_direction": null,
"file_on_server": 1
},
{
"radar_id": "KHGX",
"radar_type": "reflectivity",
"frame_time": 1495473305,
"is_analyzed": 1,
"average_speed": null,
"average_direction": null,
"file_on_server": 1
},
{
"radar_id": "KHGX",
"radar_type": "reflectivity",
"frame_time": 1495473002,
"is_analyzed": 1,
"average_speed": null,
"average_direction": null,
"file_on_server": 1
}
]
},...
What's the right way to do this? Is there an easy way to add an alias to my controller to differentiate between the two contain queries? I am using CakePHP 3.0.
Thank you!!

Since your conditions are statics, you could put them into the relationship:
Radar Site Table:
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('radar_site');
$this->setDisplayField('radar_id');
$this->setPrimaryKey('radar_id');
$this->hasMany('FutureFrame', [
'foreignKey' => 'radar_id'
]);
$this->hasMany('RadarFrameVelocity', [
'className' => 'RadarFrame',
'foreignKey' => 'radar_id',
'conditions' => [
'RadarFrame.frame_time >' => time() - 60*60,
'RadarFrame.file_on_server =' => 1,
'RadarFrame.radar_type =' => 'velocity'
]
]);
$this->hasMany('RadarFrameReflectivity', [
'className' => 'RadarFrame',
'foreignKey' => 'radar_id',
'conditions' => [
'RadarFrame.frame_time >' => time() - 60*60,
'RadarFrame.file_on_server =' => 1,
'RadarFrame.radar_type =' => 'reflectivity'
]
]);
}
Controller:
public function getRadarData()
{
$radarInfo =
$this->RadarSite->find(
'all',
[
'contain' => [
'RadarFrameVelocity' =>[
'sort' => ['RadarFrameVelocity.frame_time' => 'DESC']
],
'RadarFrameReflectivity' => [
'sort' => ['RadarFrameReflectivity.frame_time' => 'DESC']
]
]
]
);
$this->set('radarInfo', $radarInfo);
}
See also Associations - Linking Tables Together

Related

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

Log not working in yii2

i want to put a log in app.log ,My config file
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
'file' => [
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
'logFile' => '#root/console/runtime/logs/app.log',
],
]
]
in controller action
public function actionRankCalculation()
{
$allConest = Contest::find()->where('isActive = 1')->all();
Yii::trace('start calculating average revenue');
$response = [];
/** #var Contest $contest */
foreach ($allConest as $contest) {
$videoQuery = Video::find()->where('contest_id = ' . $contest->id);
$videoQuery->andWhere('isActive = 1');
$videoQuery->orderBy([
'global_likes' => SORT_DESC,
'id' => SORT_ASC,
]);
}
But Yii::trace('start calculating average revenue'); not working
You try this.Use categories. For example like below
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error'],
'categories' => ['test1'],
'logFile' => '#app/Test/test1.log',
],
And use below one in controller action
public function actionIndex(){
Yii::error('Test index action', $category = 'test1'); }
Try to set both flushInterval and exportInterval to 1 in console config:
return [
'bootstrap' => ['log'],
'components' => [
'log' => [
'targets' => [
[
'class' => 'yii\log\FileTarget',
'exportInterval' => 1,
],
],
'flushInterval' => 1,
],
],
];
It makes each log message appearing immediately in logs.

ZF3 zend-mvc generic route for many controllers in one module not working

I'm in ZF3, using the zend-mvc-skeleton and trying to configure a generic route that will match as many URLs as possible as I want to be able to create new controllers (including action methods of course), and have them immediately available.
The common approach described in the documentation is to write a route that matches the controller and action (same with ZF2).
Here is my module.config.php
namespace Application;
use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
use Zend\ServiceManager\Factory\InvokableFactory;
return [
'router' => [
'routes' => [
'home' => [
'type' => Literal::class,
'options' => [
'route' => '/',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
],
'default' => [
'type' => Segment::class,
'options' => [
'route' => '/application[/:controller[/:action]]',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
'constraints' => [
'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
],
],
],
],
],
'controllers' => [/* ... */],
'view_manager' => [/* ... */],
],
It works like a charm for http://localhost/ and http://localhost/application calling the indexAction() function of the IndexController class inside the /module/Application/src/IndexController.php file.
However, it's not working when I try to get the fooAction() function in the same Controller (i.e. IndexController). It's not resolving correctly http://localhost/application/foo. and I get the following error:
A 404 error occurred
Page not found.
The requested controller could not be mapped to an existing controller class.
Controller:
foo (resolves to invalid controller class or alias: foo)
No Exception available
Same error if I try http://localhost/bar/foo to get the fooAction() in the barController.
Do you have any idea of what's wrong with this? Any help will be appreciated. Many thanks.
The route http://localhost/application/foo won't resolve to fooAction() in the index controller, since /foo in the URL will match the controller not the action. With that route setup you would need to visit http://localhost/application/index/foo.
To get it working you'll also need to make sure you have aliased your controller in the config, e.g. assuming you have:
'controllers' => [
'invokables' => [
'Application\Controller\Index' => \Application\Controller\IndexController::class
]
],
Then alias the controller so it matches the route parameter:
'controllers' => [
'invokables' => [
'Application\Controller\Index' => \Application\Controller\IndexController::class
],
'aliases' => [
'index' => 'Application\Controller\Index'
]
],
You'll need to add aliases that match the route parameter for each controller that isn't registered using the string you want for the route, e.g. a controller Namespace\Controller\BarController should be aliased to bar, etc.
I came here with similar problem. I have created two controllers in
"Application" module, and two in new module "Account" with the same name.
Application/Controller/IndexController
Application/Controller/OverviewController
Account/Controller/IndexController
Account/Controller/OverviewController
here are my modules.config.php
module/Account/config/module.config.php
return [
'router' => [
'routes' => [
'Account-account' => [
'type' => Segment::class,
'options' => [
'route' => '/account[/][:controller[/][:action][/]]',
'defaults' => [
'__NAMESPACE__' => 'Account\Controller',
'controller' => Account\Controller\IndexController::class,
'action' => 'index',
'locale' => 'en_us'
],
],
'may_terminate' => true,
'child_routes' => [
'wildcard' => [
'type' => 'Wildcard'
],
],
],
],
],
'controllers' => [
'factories' => [
Controller\IndexController::class => AccountControllerFactory::class,
Controller\OverviewController::class => AccountControllerFactory::class,
],
'aliases' => [
'index' => IndexController::class,
'overview' => OverviewController::class
]
],
and my
module/Application/config/module.config.php
return [
'router' => [
'routes' => [
'home' => [
'type' => Literal::class,
'options' => [
'route' => '/',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
],
'Application-application' => [
'type' => Segment::class,
'options' => [
'route' => '/application[/][:controller[/][:action][/]]',
'defaults' => [
'__NAMESPACE__' => 'Application\Controller',
'controller' => Application\Controller\IndexController::class,
'action' => 'index',
'locale' => 'en_US'
],
],
'may_terminate' => true,
'child_routes' => [
'wildcard' => [
'type' => 'Wildcard'
],
],
],
],
],
'controllers' => [
'factories' => [
Controller\IndexController::class => IndexControllerFactory::class,
Controller\OverviewController::class => IndexControllerFactory::class,
],
'aliases' => [
'index' => IndexController::class,
'overview' => OverviewController::class,
]
],
With this configuration if aliases sections are commented there is a error message which says that there is invalid controller or alias (index/overview).
If there are aliases
route: "application/overview/index" goes into Account module.

Yii2 kartik typeahead - get number of suggestions

I'm using kartik's typeahead widget for Yii2 in a view:
echo \kartik\typeahead\Typeahead::widget([
'name' => 'serial_product',
'options' => [
'placeholder' => 'Filter as you type ...',
'autofocus' => "autofocus"
],
'scrollable' => TRUE,
'pluginOptions' => [
'highlight' => TRUE,
'minLength' => 3
],
'dataset' => [
[
'remote' => Url::to(['transfers/ajaxgetinventoryitemsnew']) . '?search=%QUERY',
'limit' => 10
]
],
'pluginEvents' => [
"typeahead:selected" => "function(obj, item) { add_item(item.id); return false;}",
],
]);
How can i get the number of loaded suggestions after the remote dataset is retrieved to execute a javascript function like:
displaynumber(NUMBEROFSUGGESTIONS);
After checking through the source of kartiks widget i came up with the following solution:
echo \kartik\typeahead\Typeahead::widget([
'name' => 'serial_product',
'options' => [
'placeholder' => 'Filter as you type ...',
'autofocus' => "autofocus",
'id' => 'serial_product'
],
'scrollable' => TRUE,
'pluginOptions' => [
'highlight' => TRUE,
'minLength' => 3
],
'dataset' => [
[
'remote' => [
'url' => Url::to(['transfers/ajaxgetinventoryitemsnew']) . '?search=%QUERY',
'ajax' => ['complete' => new JsExpression("function(response)
{
jQuery('#serial_product').removeClass('loading');
checkresult(response.responseText);
}")]
],
'limit' => 10
]
],
'pluginEvents' => [
"typeahead:selected" => "function(obj, item) { checkresult2(item); return false;}",
],
]);
where response.responseText is containing the response from server (json).
function checkresult(response) {
var arr = $.parseJSON(response);
console.log(arr.length);
}
With this function i can get then count of suggestions delivered from server.

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);
}
}
}
**