CakePHP Bake association problem - cakephp

I have only two tables in my database with a one-to-many relationship between them (user hasMany messages) and am trying to get basic CRUD functionality going. Bake detects the associations correctly and specifies them correctly inside the model classes, but in controllers and views it looks like Cake doesn't know anything about those associations -- I don't even get a select tag for user_id when I go add a new message. Has anyone come across this problem before? What can I be doing wrong?
Table structure appears to be fine:
CREATE TABLE users (
id int(11) NOT NULL AUTO_INCREMENT,
username varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
email varchar(255) NOT NULL,
created datetime NOT NULL,
modified datetime NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`content` varchar(255) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

If you're using the console to bake your MVC files you could try that.
First bake the model files. Then bake the controller files using scaffold.
Then bake the view files.
Finally go back and bake the controller files without scaffold.
This should get you all basic CRUD functionality with all the associations you may have.
Hope that helps ...

Related

CakePHP 3 doesn't submit the forgeign key fields value

I have the following 2 tables, and the default classes generated from
bin/cake bake all clients
bin/cake bake all clients_address
When I try and add an address, setting the "client_id" to 1, I get the following error.
Error: SQLSTATE[HY000]: General error: 1364 Field 'client_id' doesn't have a default value
If you are using SQL keywords as table column names, you can enable identifier quoting for your database connection in config/app.php.
SQL Query:
INSERT INTO clients_address (address_line_1, address_line_2, address_line_3, town, county, postcode, email, tel, contact_name, default_address) VALUES (:c0, :c1, :c2, :c3, :c4, :c5, :c6, :c7, :c8, :c9)
Is there some extra code needed to get the client_id to be submitted?
CREATE TABLE `clients` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `clients_address` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`address_line_1` varchar(30) NOT NULL,
`address_line_2` varchar(30) NOT NULL,
`address_line_3` varchar(30) NOT NULL,
`town` varchar(30) NOT NULL,
`county` varchar(30) NOT NULL,
`postcode` varchar(8) NOT NULL,
`email` varchar(50) NOT NULL,
`tel` varchar(13) NOT NULL,
`contact_name` varchar(40) NOT NULL,
`client_id` int(11) NOT NULL,
`default_address` BOOLEAN NOT NULL DEFAULT 0,
FOREIGN KEY (client_id) REFERENCES clients(id),
PRIMARY KEY (`id`)
);
INSERT INTO `clients` (`id`, `name`) VALUES (1, 'First Client');
In your clients_address Table schema client_id int(11) NOT NULL, set. But when you insert a Address then you don't set value against client_id and client_id can't be NULL
Probably solutions
Set client_id in Address insert query
In your table schema client_id allow NULL or set a default value
You need to be sure that "client_id" is sent. Check with your Debug Kit bar on History, select the POST request, and then go to Sql log.
There must be a query like this:
INSERT INTO clients_address (
client_id, address, modified
)
VALUES
(
61, 'my address', "123..."
)
If not maybe something is wrong with your Form.
Your query must be something like this:
INSERT INTO clients_address (address_line_1, ..., contact_name, CLIENT_ID, default_address)
VALUES (:c0, :c1, :c2, :c3, :c4, :c5, :c6, :c7, :c8, :c9)
If you are adding addresses standalone (addresses controller add method), you must stipulate "client_id" manually on Form, or from a hidden field based on a parameter, with Cake bake it will make a list of Clients on Add and Edit methods.
If you are adding the address from Client Form as an association, on CakePHP docs are some good examples https://book.cakephp.org/3.0/en/views/helpers/form.html#creating-inputs-for-associated-data

CakePHP Find function

i am using the following statement.
$users = $this->User->find('all');
But in the database there are only 174 rows. but the query is returning 200 rows.
When i out put the content i see that a lot of rows are repeated.
Any idea why this behavior in cakephp ?
Structure
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`first_name` varchar(255) NOT NULL,
`last_name` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`username` varchar(255) NOT NULL,
`password` varchar(40) NOT NULL,
`display_photo` varchar(255) DEFAULT NULL,
`subscription_plan_id` int(11) unsigned NOT NULL,
`company_id` int(11) NOT NULL,
`status` tinyint(2) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=177 ;
by trying to debug using this statment
echo $users = $this->User->find('count');
i get 200 itself.
Models: http://pastebin.com/p4bFPiUz
The query you are running does not nearly match the queries that CakePHP will actually execute. CakePHP will also perform all the required joins to get related data. So, this is not really a proper comparison that you are doing.
Some of your relations might return double results, like User -> CompanyA, but there could also be a User -> CompanyB relation, which would trigger 2 result rows for 1 single user.
To see the queries that Cake actually executes, use the getLog method on your datasource, like:
$ds = $this->User->getDataSource();
$log = $ds->getLog();
debug($log);
Or use something like DebugKit to get a panel with all the queries by default (when in debug mode).

How to use separate table for passwords for Auth in CakePHP?

I have a database with the following tables:
CREATE TABLE `visitors` (
`name` varchar(64) not null,
`id` int(10) unsigned not null auto_increment,
# ...more fields here
PRIMARY KEY (`id`),
UNIQUE KEY (`name`)
);
CREATE TABLE `credentials` (
`id` int(10) unsigned not null auto_increment,
`visitor_id` int(10) unsigned,
`type` enum('password','openid','google','facebook') not null,
`token` char(40) not null,
`modified` datetime,
`hint` varchar(64),
PRIMARY KEY (`id`),
KEY `visitor` (`visitor_id`),
KEY `token` (`token`)
);
After thinking about this for a awhile, I've decided that this is "right" for e.g. normalization and allowing visitors to have multiple login credentials, including multiple passwords.
However, I'd like to use Cake's ACL features, and AuthComponent assumes that hashed passwords are stored in the same table as user (visitor) information. What's the best way to work around this? Do I have to use Auth->login(), or is there a better way?
If you're using CakePHP 2.x this can be archived pretty simple by creating a new authentication handler. Looking at the existing Form handler will also help you. You should be able to extend it or copy the code into a new handler and modify it to match your needs.
Read the authentication chapter in the book. It has a section just about creating a custom handler but to get an understanding of how the whole thing works I really suggest you to read the whole page.

How to get permissions in cakePHP

First, sorry for my language skills, I am not used to writing in English. ;)
I'm trying to develop my first cakePHP application.
What I'm trying to do:
Users are in groups and groups have
access to different locations.
Users can add reservations for this
locations.
So my main problem is to find the best way to get the permissions of the user:
The user should only see the locations on which he has access.
If a user tries to add a reservation for a location, I have to check his permission for this location.
etc.
I also have moderators and admins, but I think this is a similar problem.
So, how can I do this properly? The ACL doesn't seem to be the right way - in most tutorials it controls the access to actions, not to db-rows.
What my Database looks like:
I have a user table and use the AuthComponent to manage the authentication. This works fine.
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(64) NOT NULL,
`password` varchar(64) NOT NULL,
`enabled` tinyint(1) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
)
I have a groups table for usergroups.
CREATE TABLE IF NOT EXISTS `groups` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
)
CREATE TABLE IF NOT EXISTS `groups_users` (
`group_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
UNIQUE KEY `group_id` (`group_id`,`user_id`)
)
And I have my locations.
CREATE TABLE IF NOT EXISTS `locations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL,
`adress` text NOT NULL,
`description` text,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
)
The table contains the permissions, which group has access to which location.
CREATE TABLE IF NOT EXISTS `groups_locations` (
`group_id` int(11) NOT NULL,
`location_id` int(11) NOT NULL,
UNIQUE KEY `group_id` (`group_id`,`location_id`)
)
Of course the reservations table:
CREATE TABLE IF NOT EXISTS `reservations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`location_id` int(11) NOT NULL,
`start` date NOT NULL,
`end` date NOT NULL,
`user_id` int(11) NOT NULL,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
)
THX
Are you sure that you need the groups_users table? Wouldn't each user only be able to belong to one group?
You will be able to accomplish this much easier if you just bring the group id into the users table as a foreign key
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(64) NOT NULL,
`password` varchar(64) NOT NULL,
`enabled` tinyint(1) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`group_id` int(11) NOT NULLL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
)
Then you can send to the view whether or not the user should be able to see certain information...
in your app_controller.php add the following
function beforeFilter(){
$this->set('users_role', $this->Auth->user('group_id'));
}
Now you will have a variable accessable by your view which will be $users_role... then you can perform the following in your view.
<?php if($users_role == 1 ): ?>
//show records available to admins
<?php elseif ($users_role == 2): ?>
//show records available to logged in users
<?php else : ?>
//show records for all users
<?php endif; ?>
Maybe I have a solution - I could use some feedback:
After the user logged in, I save the permissions in his Session-Variables:
function login() {
if($user = $this->Auth->user()) {
$this->User->unbindModel(array(
'hasMany' => array('Reservation'),
));
$user = $this->User->find('first', array('conditions' => array('id' => $user['User']['id']), 'recursive' => 2));
$this->Session->write('Auth.User.Group', $user['Group']);
}
I'm not sure how secure this solution is and permission changes only affects after logout, but it seems to work fine.

How do I use the TranslateBehavior in CakePHP?

There is no documentation on cakephp.org and I am unable to find one on google. Please link me some documentation or supply one!
The translate behavior is another of CakePHP's very useful but poorly documented features. I've implemented it a couple of times with reasonable success in multi-lingual websites along the following lines.
Firstly, the translate behavior will only internationalize the database content of your site. If you've any more static content, you'll want to look at Cake's __('string') wrapper function and gettext (there's some useful information about this here)
Assuming there's Contents that we want to translate with the following db table:
CREATE TABLE `contents` (
`id` int(11) unsigned NOT NULL auto_increment,
`title` varchar(255) default NULL,
`body` text,
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The content.php model then has:
var $actsAs = array('Translate' => array('title' => 'titleTranslation',
'body' => 'bodyTranslation'
));
in its definition. You then need to add the i18n table to the database thusly:
CREATE TABLE `i18n` (
`id` int(10) NOT NULL auto_increment,
`locale` varchar(6) NOT NULL,
`model` varchar(255) NOT NULL,
`foreign_key` int(10) NOT NULL,
`field` varchar(255) NOT NULL,
`content` mediumtext,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Then when you're saving the data to the database in your controller, set the locale to the language you want (this example would be for Polish):
$this->Content->locale = 'pol';
$result = $this->Content->save($this->data);
This will create entries in the i18n table for the title and body fields for the pol locale. Finds will find based on the current locale set in the user's browser, returning an array like:
[Content]
[id]
[titleTranslation]
[bodyTranslation]
We use the excellent p28n component to implement a language switching solution that works pretty well with the gettext and translate behaviours.
It's not a perfect system - as it creates HABTM relationships on the fly, it can cause some issues with other relationships you may have created manually, but if you're careful, it can work well.
For anyone searching the same thing, cakephp updated their documentation. For Translate Behavior go here..

Resources