Cakephp not loading belongsToMany association beetween two Models - cakephp

I just started my project with cakephp3.0. I startet with the Users. I put a function isAuthorized in the UsersController which should validate if the User is permitted to view other users, to edit other users and so on.
I created a table "userprivileges" where all possible privileges, which can be assigned to a user are listed.
The table is "userprivileges" is:
id | controller | function
Next I want to group the priviliges into roles.
I created following table: "userprivilegeroles"
id | role
Next I created the BTM-Table: "userprivileges_userprivilegeroles"
id | userprivilegerole_id | userprivilegerole_id
The User is linked to the userprivilegeroles table, so I need to search if the requested controller/function is view-able for the user.
I tried like this:
$this->loadModel('Userprivilegeroles');
$userprivilegeroles = $this->Userprivilegeroles->find('all', [
'conditions' => [
'Userprivileges.controller' => $this->request->controller,
'Userprivileges.function' => $this->request->action],
'contain' => ['Userprivileges']
]);
Now this fails with following error:
Userprivilegeroles is not associated with Userprivileges.
There is a little more description, where I can search for the problem prompted to the 500 page.
The class for the specified table does not exist.
The Table was created with a typo: TableRegistry::get('Atricles');
The class file has a typo in the name or incorrect namespace: class Atricles extends Table
The file containing the class has a typo or incorrect casing: Atricles.php
The Table was used using associations but the association has a typo: $this->belongsTo('Atricles')
The table class resides in a Plugin but no plugin notation was used in the association definition.
Lets go for it one by one:
The class for the specified table does not exist.
It does:
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class UserprivilegerolesTable extends Table
{
public function initialize(array $config){
$this->belongsToMany('Userprivileges', [
'joinTable' => 'userprivileges_userprivilegeroles',
'className' => 'Userprivileges'
]);
}
}
Next
The Table was created with a typo: TableRegistry::get('Atricles');
No.
The class file has a typo in the name or incorrect namespace: class Atricles extends Table
no.
The file containing the class has a typo or incorrect casing: Atricles.php
no. its in src/Model/Table/Userprivilegeroles.php
The Table was used using associations but the association has a typo: $this->belongsTo('Atricles')
See above. I don't see a typo.
The table class resides in a Plugin but no plugin notation was used in the association definition.
As described, I called it from the UsersController.
Can anyone help me finding, why the association doesn't work as expected? Maybe I am to much stuck in the cakephp 2.0 ORM, but I am almost sure that I did the association like described in the cookbook.
I've searched already and found some other issues with the belongstomany association. But they all handle problems with saving data. I want to search data, so I see no connection there.

Actually the filename is incorrect, filenames must match classnames (PSR-4), so it should be UserprivilegerolesTable.php. The warning notes may need some fixing or further explanation, as they are a little misleading by not using the Table suffix.
Once you've fixed that you'll stumble over the next problem, contained belongsToMany and hasMany associations cannot be used in conditions, as they are retrieved in a separate query, you'll have to use matching() instead.
Cookbook > Database Access & ORM > Query Builder > Filtering by Associated Data
How to filter by conditions for associated models?

Related

setTable not working as expected with a legacy database in CakePHP 3.x

I have a legacy (not written to Cake naming conventions) database on a CakePHP 3.5.13 application.
One of the database tables is named article_95.
When I attempted to bake the application it's showing the entity name as Article95. It's then producing loads of error messages saying:
Table 'article95' doesn't exist
So I've read CakePHP error: cake bake is using the wrong table name and How to use table name different then in database in cake php 3 and decided to do it manually using setTable().
So I have created src/Model/Table/Article95Table.php with the following code in it:
namespace App\Model\Table;
use Cake\ORM\Table;
class Article95Table extends Table
{
public function initialize(array $config)
{
$this->setTable('article_95');
}
}
But it won't seem to recgonise this. In a controller I've created a testing method and done the following:
Cache::clear(false);
$Article95 = TableRegistry::get('Article95');
debug($Article95->find()->list());
But it's coming up with:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'article95' doesn't exist
It's as if it's not reading the setTable() method.
I've used the Cache::clear(false) to ensure it's not being cached and have inspected the tmp/cache directory for any suspect files - nothing relevant in there.
Any ideas?
Equally if I change my debug() statement to just debug($Article95) I get the following, with the wrong table name:
object(Cake\ORM\Table) {
'registryAlias' => 'Article95',
'table' => 'article95',
'alias' => 'Article95',
'entityClass' => '\Cake\ORM\Entity',
'associations' => [],
'behaviors' => [],
'defaultConnection' => 'default',
'connectionName' => 'default'
}
Incidentally, if I put a non-existent class/entity name in TableRegistry::get() - for example TableRegistry::get('dsfdsfdsfsd'); it won't produce an error message but will show the object above with the non-existent table name. Surely this is also wrong?
I've managed to get this working so wanted to share my solution.
After some trial and error it seems that appending an 's' to the class name (presumably to make Cake consider it plural?) works.
So I renamed the Table class to Article95sTable.php and then used the following code inside:
class Article95sTable extends Table
{
public function initialize(array $config)
{
$this->setTable('article_95');
}
}
Now when I call the following it picks up the table correctly:
$foo = TableRegistry::get('Article95s');
debug($foo);
To confirm this is working if I rename the table inside my setTable() method the output will change accordingly.

Yii2 + Redis as Database

I want to use Yii2 and redis as database.
So far, I got Redis ActiveRecord Class for Yii2 from Here.
link1
link2
but, I got a problem. WHY THIS CLASS ADDS ANYTHING AS HASH IN REDIS????
Above that I cant Find in which pattern it Insert data. I add one user and it will add a user under user:xxx namespace and another record under s:user:xxx and so on but none of theme has any fields that i defined in attributes!! only contain IDs.
I know that a Key-value type database and RDBMS are different and also know how can implement relation like records in Redis, but I don't know why it will only save IDs.
I could not find any example of using redis ActiveRecords so far.
There is one in here and its not good enough.
So here is my main wuestion: how can add data to redis Using activeRecords and different data types In YII2?
And if its impossible with ActiveRecords what is the best solution? in this case
ANOTHER QUESTION: is it possible to use a Model instead and write my own model::save() method? and what is the best data validation solution at this rate?
Actually I want to make a telegram bot, so i should get messages and send them in RabitMQ and get data in a worker, do the process and save results to Redis, and finally send response to user through the RabitMQ.
So I need to do a lot of validations AND OF COURSE AUTHENTICATIONS and save and select and range and save to sets an lists and this and that ....
I want a good way to make Model or active record or the proper solution to validation, save and retrieve data to Redis and Yii2.
Redis DB can be declared as a cache component or as a database connection or both.
When it is declared as a cache component (using the yii/redis/cache) it is accessible within that component to store key/value pairs as shown here.
$cache = Yii::$app->cache;
// try retrieving $data from cache
$data = $cache->get($key);
// store $data in cache so that it can be retrieved next time
$cache->set($key, $data);
// one more example:
$access_token = Yii::$app->security->generateRandomString();
$cache->add(
// key
$access_token,
// data (can also be an array)
[
'id' => Yii::$app->user->identity->id
'name' => Yii::$app->user->identity->name
],
// expires
60*60*3
);
Also other components may start using it for caching proposes like session if configured to do so or like the yii\web\UrlManager which by default will try to cache the generated URL rules in whatever valid caching mechanism defined under the config file's cache component as explained here. So it is normal to find some stored data other than yours in that case.
When Redis is declared as a DB connection like in the links you provided which means using the yii\redis\Connection class you can make your model extending its \yii\redis\ActiveRecord class as any other ActiveRecord model in Yii. The only difference I know so far is that you need to define your attributes manually as there is no DB schema to parse for NoSQL databases. Then just define your rules, scenarios, relations, events, ... as any other ActiveRecord model:
class Customer extends \yii\redis\ActiveRecord
{
public function attributes()
{
return ['id', 'name', 'address', 'registration_date'];
}
public function rules()
{
return [
['name', 'required'],
['name', 'string', 'min' => 3, 'max' => 12, 'on' => 'register'],
...
];
}
public function attributeLabels() {...}
...
}
All available methods including save(), validate(), getErrors(), ... could be found here and should be used like any other ActiveRecord class as shown in the official guide.

Cakephp find('list') and Translate behavior

I'm using CakePHP 3.
I have an accounts table. Each Account belongs to a Country. Countries table is internationalized using Translate Behavior, so it can store countries names for each language.
class CountriesTable extends Table
{
public function initialize(array $config)
{
$this->table('countries');
$this->displayField('name');
$this->primaryKey('id');
$this->addBehavior('Translate', ['fields' => ['name']]);
}
Note that "name" field is internationalized through the Translate behavior, so it's not present in the countries table.
Then in the AccountsController I want to get the list of the countries using the active language:
....
$countries = $this->Accounts->Countries->find('list', ['limit' => 200]);
.....
The problem is that it's not getting the translations from the i18n table as expected. I already have coded the maintentance actions for the internationalized countries table, and everything is working OK, but this simple find('list') is not working.
One more detail: in the Mysql logs I see it has launched this query. Note that the countries table has only two fields: id and code, but the displayField is name.
SELECT Countries.id AS `Countries__id`, Countries.code AS `Countries__code` FROM countries Countries LIMIT 200
btw, it's getting really difficult to switch from 2.x to 3.x.
I found the problem after a lot of investigating. If my case, accounts table is not internationalized, but the related table countries it is.
The problem is that I didn't set the locale in the Accounts controller. After putting this line before the finds, everything worked fine:
I18n::locale('spa');
So I guess that if you don't stablish the locale using I18n, Translate behavior won't work at all. I should load it in AppController to make it application wide available.
Thanks a lot for your help!!

CakePHP - toMany relationship not working with UUIDs

I have the following model class:
class Property extends AppModel
{
var $name = 'Property';
var $hasMany = array(
'Inventory' => array(
'className' => 'Inventory',
'foreignKey' => 'property_id'
)
);
}
My database schema is set so that the id fields are all set to CHAR(36) so that CakePHP generates UUIDs for each entity. When I attempt to perform a find on my Property entity, it doesn't seem to be adding the necessary join to retrieve any related Inventories. Does anyone have any experience with this issue?
Thanks!
UUIDs will have nothing to do with it, I have a very similar model setup and use UUIDs everywhere.
You technically don't need those className and foreignKey declarations in there, as you seem to be following the CakePHP convention :)
I'd remove those lines, check your database for actual inventories with product IDs. If not, post the find()s you're doing.
The answer turns out to be somewhat stupid.
I'd migrated this from a CakePHP 2.0 install to a CakePHP 1.3 install (couldn't update PHP on the server I was using), and all of my model classes still had uppercase first characters. Changing the file from Property.php to property.php fixed it.

Computing table name from model name

In my CakePHP application, I have a model like this:
class Duck extends AppModel {
var $name = 'Duck';
function get_table_name() {
$tbl_name = //compute default table name for this model
}
}
I would like to write the function get_table_name() that outputs the default table name for the model. For the example above, it should output ducks.
EDIT:
Several people have pointed out the use of $this->table.
I did small testing and found out the following:
In the question as I have put above, $this->table indeed contains the table name.
However, actually, my code looked more like this:
class Duck extends Bird {
var $name = 'Duck';
function get_table_name(){
$tbl_name = //comput default table name for this model
}
}
class Bird extends AppModel {
}
In this case $this->table is empty string.
I went with this approach because I wanted to share some code between two of my models. Looks like this is not a good way to share code between models which need some common functionality.
You're looking for the Inflector class.
Inflector::tableize($this->name)
(tableize calls two Inflector methods to generate the table name: underscore() and pluralize())
Edit:
According to the source code, $this->table should contain the name of the table that CakePHP will use for the model, but in my experience this isn't always set. I'm not sure why.
To get the name of the table that the model is currently using, you can use: $this->table. If you don't manually change the model's table conventions, this may be the most useful in the case of CakePHP ever changing its conventions to use table names using something other than Inflector.
CakePHP's Inflector
function get_table_name() {
$tbl_name = Inflector::pluralize($this->name);
}
OR the tableize method
function get_table_name() {
$tbl_name = Inflector::tableize($this->name);
}
Edit
This also addresses the apparent "ghost" issue with $this->table in the Model.
Digging around in the __construct for Model I discovered two things:
Cake uses Inflector::tableize() to get the table name. This alone is enough to warrant using tableize over pluralize. You'll get consistent results.
$this->table is not set by the Model::__construct() unless $this->useTable === false AND $this->table === false.
It appears that if you know you haven't set $this->useTable to false you should be able to use this over $this->table. Admittedly though I only briefly scanned the source and I haven't really dug deep enough to say why $this->table isn't working sometimes.
To get the full table name for a model you have to take the table prefix into account.
$table = empty($this->table) ? Inflector::tableize($this->name) : $this->table;
$fullTableName = $this->tablePrefix . $table;
I used to use inflector to get the table name from model's name
$tableName = Inflector::pluralize(Inflector::underscore($model));
but this is not really universal, using useTable looks better, by default it will contain table's name by convention, and if you have a table that does not match the conventions, then you should manually specify it by useTable. So, in both cases the result will be correct
$this->User->useTable

Resources