CakePHP not respecting foreignKey name - mysql

I have an articles table with a column tagging_id that needs to reference the id column in a table called tracking_categories as a foreign key.
My Article.php file has the following:
class Article extends AppModel {
public $controllerPath = 'articles';
public $useTable = 'articles';
var $binds = array(
'TrackingCategory' => array(
'bindType' => 'belongsTo',
'className' => 'TrackingCategory',
'foreignKey' => 'tagging_id'
),
'Channel' => array(
'bindType' => 'belongsTo',
'className' => 'Channel',
'foreignKey' => 'channel_id'
)
);
My TrackingCategory.php looks like this:
class TrackingCategory extends AppModel {
public $useTable = 'tracking_categories';
I have the following in the AppModel which Article.php inherits:
function expects($binds, $reset = false) {
if (func_num_args() > 2 || !is_bool($reset)) {
throw new InvalidArgumentException('Did you mean to pass an
array of binds?');
}
if (!is_array($binds)) {
$binds = array($binds);
}
foreach ($binds as $bind) {
if (isset($this->binds[$bind])) {
$tmp = array($this->binds[$bind]['bindType'] => array($bind => $this->binds[$bind]));
$this->bindModel($tmp, $reset);
}
}
}
I am getting the following error:
Database Error
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Article.tracking_category_id' in 'field list'
It is still looking for the column named parsed via CakePHP's naming convention rather than the name that I explicitly set via the foreignKey value. What am I missing here?

The right syntax should be:
public $belongsTo = array(
'TrackingCategory' => array(
'className' => 'TrackingCategory',
'foreignKey' => 'tagging_id',
)
);
For more information, see CakePHP 2.x - Associations: Linking Models Together - belongsTo.

Related

CakePHP 3 is saying a table doesn't exist in another database when using an ORM query

I have an application in CakePHP 3.5.13 which uses four different databases.
In my config/app.php I have configured two of these as follows:
'Datasources' => [
'default' => [ 'database' => 'sdb5_hub_subdb', ... ],
'db_orgs' => [ 'database' => 'dev_hub_subdb_orgs', ... ]
];
So this means there are 2 databases with the names sdb5_hub_subdb and dev_hub_subdb_orgs.
I'm attempting to do a query which does a join on a table in each database.
I have my Table entities configured as follows:
// src/Model/Table/TblListsSubstancesCommentsTable.php
public function initialize(array $config)
{
$this->belongsTo('Substances', [
'foreignKey' => 'substance_id',
'joinType' => 'INNER'
]);
}
public static function defaultConnectionName()
{
return 'db_orgs';
}
// src/Model/Table/SubstancesTable.php
public function initialize(array $config)
{
$this->belongsTo('TblListsSubstancesComments', [
'foreignKey' => 'substance_id'
]);
}
When I attempt to do the following with the ORM:
$query = $Substances->find()->select(['id' => 'Substances.id'])->distinct();
if ($this->request->getData('org_information')) {
$org_information = $this->request->getData('org_information');
$query = $query->matching('TblListsSubstancesComments', function ($q) use ($org_information) {
return $q->where(['TblListsSubstancesComments.comment LIKE' => '%'.$org_information.'%' ]);
});
}
It's producing an SQL error:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'sdb5_hub_subdb.tbl_lists_substances_comments' doesn't exist
I don't understand this because the table definitely exists. Furthermore my TblListsSubstancesCommentsTable.php has defaultConnectionName() in it to specify that it should use the db_orgs connection. So I assume it must know to go to the second database to load that table? It's like it's looking in the default database and not finding it, but I don't know why, because the Table entity is telling it where it needs to look.

Insert or update in laravel?

What would be the best way to set this query? i can't seem to find any documentation on an actual insert or update that works, I'm hoping to do it via the Eloquent model but can't seem to get it working
any help would be appreciated.
DB::table('questions')->insert([
'marital_status' => $request->marital_status,
'job_title' => $request->job_Title,
'industry' => $request->industry,
'occupation' => $request->occupation,
'paye' => $request->paye,
'self_employed' => $request->self_employed,
'child_benefit' => $request->child_benefit,
'work_home' => $request->work_home,
'own_transport' => $request->own_transport,
'company_vehicle' => $request->company_vehicle,
'annual_income' => $request->annual_income,
'pay_memberships' => $request->pay_memberships,
'income_benefits' => $request->income_benefits,
'printer' => $request->printer,
'contact' => $request->contact,
'share' => $request->share,
'terms' => $request->terms,
'user_id' => $user
]);
here is my Model for Questions
<?php
namespace App;
class Questions extends Model
{
protected $fillable = [
'marital_status',
'job_title',
'industry',
'occupation',
'paye',
'self_employed',
'child_benefit',
'work_home',
'own_transport',
'company_vehicle',
'annual_income',
'pay_memberships',
'income_benefits',
'printer',
'contact',
'share',
'terms',
'user_id'
];
public function users(){
return $this->hasOne('App\User');
}
}
Use Eloquent with mass assignment:
Question::updateOrCreate($request->all()->put('user_id', $user));
Or:
$question = Question::firstOrNew('some_id', $someId);
$question->fill($request->all()->put('user_id', $user))->save();
Don't forget to fill $fillable array with all properties you want to persist:
class Question extends Model
{
protected $fillable = [
'marital_status',
'job_title',
'industry',
'occupation',
'paye',
'self_employed',
'child_benefit',
'work_home',
'own_transport',
'company_vehicle',
'annual_income',
'pay_memberships',
'income_benefits',
'printer',
'contact',
'share',
'terms',
'user_id'
]
Update
If put() method doesn't work for some reason, try this:
$request->merge(['user_id' => $user]);
And then just use $request->all()
Or:
$requestData = $request->all();
$requestData['user_id'] = $user;
And then use $requestData

How to implement filtering of a CGridView table for an ENUM columnt in Yii?

In a Yii application I display the data as a table. I use CGridView, that deals for me with (search based) data filter and sorting. It works.
Now I added an ENUM column status to the user database table. In the grid I can sort and filter this, but only by the value in the table. Well, it makes sense. But the user don't actually know, how it's saved n the database and works with (and wants to sort and filter by) labels.
Is there a way to provide sorting and filtering by custom labels for an ENUM database table column in Yii (using a CActiveRecord model and a CGridView generated data grid)?
database table
CREATE TABLE `users` (
`userid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`email` varchar(45) NOT NULL,
...
`status` enum('processed', 'waiting') NOT NULL,
PRIMARY KEY (`userid`),
UNIQUE KEY `email_UNIQUE` (`email`)
);
User model
/**
* ...
* #property string $userid
* #property string $email
* ...
* #property string $status
*/
class User extends CActiveRecord
{
public function tableName()
{
return 'users';
}
public function getFirstAndLastName(){
return CHtml::encode($this->firstname." ".$this->lastname);
}
public function rules()
{
return array(
...
array('userid, email, status', 'safe', 'on'=>'search'),
);
}
public function attributeLabels()
{
return array(
'userid' => 'Userid',
'email' => 'Email',
...
'status' => Yii::t('app', 'status'),
);
}
public function relations()
{
return array(
...
);
}
public function search()
{
$criteria=new CDbCriteria;
$criteria->compare('userid',$this->userid,true);
$criteria->compare('email',$this->email,true);
...
$criteria->compare('status',$this->status,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
"pagination"=>array(
"pageSize"=>25
)
));
}
public static function model($className=__CLASS__)
{
return parent::model($className);
}
protected function beforeSave()
{
return parent::beforeSave(); // TODO: Change the autogenerated stub
}
public function getProcessingStatus()
{
return Yii::t('app', $this->processing_status);
}
}
UserController
class UserController extends Controller
{
...
/**
* Manages all models.
*/
public function actionAdmin()
{
// Yii::app()->cache->flush();
$model=new User('search');
$model->unsetAttributes(); // clear any default values
if(isset($_GET['User']))
$model->attributes=$_GET['User'];
$this->render('admin',array(
'model'=>$model,
));
}
...
}
view
...
<?php
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'user-grid',
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
'email',
'status',
array(
'class' => 'CButtonColumn',
'template' => '{view}{update}',
'buttons' => array(
'view' => array(
)
),
),
),
));
Model:
class User extends CActiveRecord {
const STATUS_PROCESSED = 'processed';
const STATUS_WAITING = 'waiting';
public static function getStatusList(){
return array(
self::STATUS_PROCESSED => 'Processed',
self::STATUS_WAITING => 'Waiting',
);
}
public function getStatusValue(){
$list = self::getStatusList();
return array_key_exists( $this->status, $list ) ? $list[ $this->status ] : 'Undefined';
}
}
View:
$this->widget('zii.widgets.grid.CGridView', array(
// other params
'columns' => array(
// other columns
array(
'name' => 'status',
'value' => '$data->getStatusValue()',
'filter' => User::getStatusList(),
),
)
));
That's all
You can specify a custom filter in your CDataColumn, by setting the filter property to be an array. It will result in a dropdown list filter instead of a text field filter.
Example:
<?php
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'user-grid',
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
'email',
array(
'name' => 'status',
'filter' => array(
'processed' => 'Processed',
'waiting' => 'Waiting',
),
),
array(
'class' => 'CButtonColumn',
'template' => '{view}{update}',
'buttons' => array(
'view' => array(
)
),
),
),
));
Also check out the docs

CakePHP find Queries with aliases SQLSTATE[42S22] Error

I'm having a weird problem with my relationships/aliases in CakePHP and now its preventing me from accessing my data correctly.
I have:
User hasMany CreatorModule (alias for Module)
User HABTM LearnerModule (alias for Module)
Module belongsTo Creator (alias for User)
Module HABTM Learner (alias for User)
And I'm trying to call:
$id = $this->Module->User->findByEmail($email);
$modules = $this->Module->findByUserId($id['User']['id']);
The queries that get generated aren't correct - the table-alias is wrong. I'm not sure which of the above is responsible but I get:
SELECT
`Creator`.`id`,
`Creator`.`email`,
`Creator`.`organization`,
`Creator`.`name`,
`Creator`.`password`,
`Creator`.`verified`,
`Creator`.`vcode`
FROM
`snurpdco_cake`.`users` AS `Creator`
WHERE
`User`.`email` = 'foo#example.com' # <--
LIMIT 1
I figured out that the error is that CakePHP should change 'User' in the WHERE clause to Creator, but doesn't, even if I use the alias. How do I complete this query correctly.
Further, as a related problem, I find that I can no longer use User in my model calls etc now that I have defined aliases. Is there a way around this?
EDIT: As requested, here is my model code defining the aliases:
class User extends AppModel {
public $name = 'User';
public $uses = 'users';
public $hasMany = array(
'OwnedModule' => array(
'className' => 'Module',
'foreignKey' => 'user_id',
'dependent' => true
));
public $hasAndBelongsToMany = array(
'LearnerModule' => array(
'className' => 'Module',
'joinTable' => 'modules_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'module_id',
'unique' => 'keepExisting',
));
//The rest of the Model
}
//Different file, condensed here for spacing
class Module extends AppModel {
public $name = 'Module';
public $belongsTo = array(
'Creator' => array(
'className' => 'User'));
public $hasAndBelongsToMany = array(
'Learner' => array(
'className' => 'User',
'joinTable' => 'modules_users',
'foreignKey' => 'module_id',
'associationForeignKey' => 'user_id',
'unique' => 'keepExisting',
));
//The rest of the Model
}
try
$id = $this->Module->Creator->find('first',
array('conditions' => array('Creator.email' => $email)
);
$modules = $this->Module->findByCreatorId($id['User']['id']);

Zend CASCADE on many-to-many relationship

I'm using the default Zend_Db_Table_Abstract to set the relationships. These are my models:
Model_Table_Project:
protected $_name = 'projects';
protected $_dependentTables = array('Model_Table_ProjectStage');
protected $_referenceMap = array(
'User' => array(
'columns' => 'userId',
'refTableClass' => 'Model_Table_User',
'refColumns' => 'id'
)
);
public function deleteProject($id)
{
$row = $this->find($id)->current();
if ($row)
{
$row->delete();
}
else
{
throw new Zend_Exception('Cannot delete project.');
}
}
Model_Table_Stage:
protected $_name = 'stages';
protected $_dependentTables = array('Model_Table_ProjectStage');
Model_Table_ProjectStage:
protected $_name = 'projectstage';
protected $_referenceMap = array(
'Project' => array(
'columns' => 'projectId',
'refTableClass' => 'Model_Table_Project',
'refColumns' => 'id',
'onDelete' => self::CASCADE,
),
'Stage' => array(
'columns' => 'stageId',
'refTableClass' => 'Model_Table_Stage',
'refColumns' => 'id',
'onDelete' => self::CASCADE,
)
);
Now when I want to delete a project, using the deleteProject() method in Model_Table_Project, it deletes the entry in the Project table and the entries in the ProjectStage table, but it doesn't delete the entries in the Stage table.
What am I doing wrong?
I've found the solution. It was a thinking error of myself. The solution I now use is:
In the Table model I call $row->delete() and in the Row model I declared a delete method:
public function delete()
{
$stages = $this->getStages();
foreach ($stages as $stage)
{
$stage->delete();
}
return parent::delete();
}
This way it first delete all relating stages and then it deletes itself.