Foreign key constraint when using drop down in CakePHP - php

I am using a drop down box with a foreign key relationship. I have got the drop down filling in the correct values but the only problem is when I add a user there is a foreign key constraint. But I can make users if I just use the normal input box and type an id that exists in the other table.
For example when I enter the id with this in my add.ctp, it works:
echo $this->Form->input('location');
but when I use this it doesn't
echo $this->Form->input('location_id', array('type' => 'select', 'options' => $CompanyLocations));
This is my add function in my UsersController
public function add()
{
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->data);
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
$CompanyLocations= $this->Users->CompanyLocations->find('list');
$this->set(compact('CompanyLocations'));
$this->set(compact('user'));
$this->set('_serialize', ['user']);
This is in my UsersTable
$this->belongsTo('CompanyLocations');
and my CompanyLocationsTable
public function initialize(array $config)
{
parent::initialize($config);
$this->table('company_locations');
$this->displayField('location_name');
$this->primaryKey('location_id');
$this->belongsTo('Locations', [
'foreignKey' => 'location_id',
'joinType' => 'INNER'
]);
}
and my MySQL code
CREATE TABLE IF NOT EXISTS southpac_team.company_locations (
location_id INT NOT NULL AUTO_INCREMENT,
location_name VARCHAR(45) NULL,
PRIMARY KEY (location_id))
ENGINE = InnoDB;
DROP TABLE IF EXISTS southpac_team.users ;
CREATE TABLE IF NOT EXISTS southpac_team.users (
id INT NOT NULL AUTO_INCREMENT,
username VARCHAR(20) NOT NULL,
password VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
department INT NULL,
mobile VARCHAR(255) NULL,
email VARCHAR(255) NULL,
extension INT NULL,
lame_number INT NULL,
spa_auth_number VARCHAR(15) NULL,
creation_date DATE NULL,
picture VARCHAR(255) NULL,
employed TINYINT(1) NOT NULL,
location INT NOT NULL,
PRIMARY KEY (id),
INDEX get location_idx (location ASC),
CONSTRAINT get location
FOREIGN KEY (location)
REFERENCES southpac_team.company_locations(location_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;

Naming conventions
You are not following the naming conventions, by default the foreign key name for a belongsTo association is the singular underscored variant of the association alias, postfixed with _id so in the case of CompanyLocations that would be company_location_id, not just location.
echo $this->Form->input('company_location_id');
Also the variable holding the list should use camel casing, then you don't even need to specify it via the options argument:
$companyLocations= $this->Users->CompanyLocations->find('list');
$this->set(compact('companyLocations'));
Change the association defaults
If you are working with a legacy database that you cannot modify, then you need to configure CakePHP accordingly, ie specify the custom foreign key via the options argument of Table::belongsTo().
$this->belongsTo('CompanyLocations', [
'foreignKey' => 'location'
]);
Bake gets confused
The belongsTo association in CompanyLocationsTable looks fishy too, unless you really have a LocationsTable that should be associated with CompanyLocationsTable via:
company_locations.location_id > locations.primary_key
I guess you've created the model via bake, which treated location_id as a foreign key since it matches the default foreign key naming scheme for a belongsTo association.
See also
Cookbook > CakePHP at a Glance > Conventions > Model and Database Conventions
Cookbook > Database Access & ORM > Associations - Linking Tables Together > BelongsTo Associations
Cookbook > Views > Helpers > Form > Creating Form Controls
Cookbook > Views > Helpers > Form > Creating Inputs for Associated Data

Related

multiple insertion in cakephp 3.8

I should make a multiple listing. where two fields have the same values in common. For example, if I enter Inter, Milan, Juve they will have nationality_season and series_season as common fields. Furthermore, the user should decide on the number of advertisements to be made.
only that when I go to insert everything: Error: SQLSTATE[HY000]: General error: 1364 Field 'club_id' doesn't have a default value
i have this database
DROP TABLE IF EXISTS `championships`;
CREATE TABLE IF NOT EXISTS `championships` (
`id` int(11),
`club_id` int(11) NOT NULL,
`season` varchar(9) NOT NULL DEFAULT ' ',
`nationality_championship` varchar(50) NOT NULL DEFAULT '0',
`championship_series` varchar(50) NOT NULL DEFAULT '0',
`penal` int(11) DEFAULT '0',
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `clubs`;
CREATE TABLE IF NOT EXISTS `clubs` (
`id` int(11),
`name` varchar(35) NOT NULL DEFAULT ' '
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
ALTER TABLE clubs
ADD PRIMARY KEY (id),
MODIFY id int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE championships
ADD PRIMARY KEY (id),
MODIFY id int(11) NOT NULL AUTO_INCREMENT,
ADD FOREIGN KEY(club_id) REFERENCES clubs(id);
the function that manages the insertion is the add function in the championships controller:
public function add()
{
$championship = $this->Championships->newEntity();
if ($this->request->is('post')) {
$championship = $this->Championships->patchEntity($championship, $this->request->getData());
if ($this->Championships->save($championship)) {
$this->Flash->success(__('The championship has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The championship could not be saved. Please, try again.'));
}
$clubsUnmated=$this->Championships->Clubs->find('list',['keyField' => 'id',
'valueField' =>['nome_societa']])->notMatching('Championships');
$this->set(compact('championship', 'clubsUnmated'));
}
extract code add.ctp:
<div class="championships form large-9 medium-8 columns content">
<?= $this->Form->create($championship) ?>
<fieldset>
<legend><?= __('Add Championship') ?></legend>
<?php
echo $this->Form->control('season',['class'=>'form-control']);
echo $this->Form->control('nazionalità_campionato',['class'=>'form-control']);
echo $this->Form->control('serie_campionato',['class'=>'form-control']);
echo $this->Form->control('club_id', ['options' => $clubsUnmated,'data-role'=>'tagsinput','type'=>'text','placeholder'=>'aggiungi squadre','class'=>'form-control']);
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
For one thing, your form controls have different field names than your database schema is showing for nationality_championship and championship_series. That's not causing this problem, but it'll be a problem soon.
The main thing here is that it looks like you're going to be getting an array of values from your club_id, but the field in the database is a single value. If you want to stick with this database schema, you're going to have to loop over that array (which should probably be called something else in your form, to avoid issues when creating your entities to save). For example, if you rename that control to clubs, then it might look something like this:
if ($this->request->is('post')) {
$championships = [];
$data = $this->request->getData();
foreach ($data['clubs'] as $club_id) {
$championships[] = $this->Championships->newEntity(array_merge($data, compact('club_id')));
}
if ($this->Championships->saveMany($championships)) {
$this->Flash->success(__('The championship has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The championship could not be saved. Please, try again.'));
}
But even that probably isn't what you really want, as it's repeating the championship data many times in your database. I think what you really want is not to have club_id in that table at all, but rather have a championships_clubs join table, and then you can probably just name your club_id control as clubs._ids and your original patch and save code will work fine to create the whole record structure.

Cakephp 3 create entry, set custom primary field

I have a roles table. Looks like this:
CREATE TABLE `roles` (
`role` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
`permissions` longtext COLLATE utf8_unicode_ci
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
ALTER TABLE `roles`
ADD PRIMARY KEY (`role`),
ADD UNIQUE KEY `role` (`role`);
Now cake is not recognizing it as a "normal" field, so it doesn't give out any input field.
I fixed my view with this:
// src/Template/Admin/Roles/add.ctp
echo $this->Form->control('name', ['class' => 'form-control']);
And now the workaround in my controller:
// src/Controller/Admin/RolesController.ctp
$roleData = $this->request->getData();
$roleData['role'] = strtolower($roleData['name']);
unset($roleData['name']);
$role = $this->Roles->patchEntity($role, $roleData);
if ($this->Roles->save($role)) {
$this->Flash->success(__('The role has been saved.'));
}
It saves the entry, but doesn't fill up anything in the database row role. Am I missing something?
If you are using patchEntity then you cannot assign non assignable fields and your primary key is more than likely not an assignable key by default. You can change it in the entity which should allow the form to show it will allow patch entity to work correctly.
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Role extends Entity
{
protected $_accessible = [
'role' => true,
'permissions' => true,
'*' => false,
];
}
https://book.cakephp.org/3.0/en/orm/saving-data.html#changing-accessible-fields
https://book.cakephp.org/3.0/en/orm/entities.html#mass-assignment
In RolesTable.php there should be something like this:
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('roles');
$this->setPrimaryKey('role');
}
so CakePHP would use 'role' as PrimaryKey.

Yii joining two table using relations in model

Hi I have these two tables that I want to join using relations in Yii, The problem is Im having a hard time figuring out how Yii relation works.
picturepost
id
title
link_stat_id
linkstat
id
link
post_count
I also have a working SQL query. This is the query I want my relation to result when I search when I want to get picturepost
SELECT picturepost.id, picturepost.title,linkstat.post_count
FROM picturepost
RIGHT JOIN linkstat
ON picturepost.link_stat_id=linkstat.link;
I want something like this when I search for a post.
$post = PicturePost::model() -> findByPk($id);
echo $post->linkCount;
Here's my table for extra info:
CREATE TABLE IF NOT EXISTS `picturepost` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`title` text COLLATE utf8_unicode_ci DEFAULT NULL,
`link_stat_id` char(64) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=MyISAM;
CREATE TABLE IF NOT EXISTS `linkstat` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`link` char(64) COLLATE utf8_unicode_ci NOT NULL,
`post_count` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `post_count` (`post_count`),
KEY `link_stat_id` (`link`)
) ENGINE=InnoDB;
Thanks in advance I hope I explained it clearly.
There are a few tutorial regarding this, and I won't repeat them, but urge you to check them out.
The easiest starting point will be to create your foreign key constraints in the database, then use the Gii tool to generate the code for the model, in this case for the table picturepost.
This should result in a class Picturepost with a method relations(),
class Picturepost extends {
public function relations()
{
return array(
'picturepost_linkstats' => array(self::HAS_MANY,
'linkstat', 'link_stat_id'),
);
}
This links the 2 tables using the *link_stat_id* field as the foreign key (to the primary key of the linked table).
When you are querying the table picturepost, you can automatically pull in the linkstat records.
// Get the picturepost entry
$picturepost = PicturePost::model()->findByPk(1);
// picturepost_linkstats is the relationship name
$linkstats_records = $picturepost->picturepost_linkstats;
public function relations()
{
return array(
'linkstat' => array(self::HAS_ONE, 'Linkstat', array('link_stat_id'=>'link')),
);
}
More on yii relations.
This assumes that you have an active record model Linkstat that represents data in table linkstat.

Kohana 3.2 validate composite primary key

I have following table
create table `groupusers`(
`id` int not null auto_increment,
`user` varchar(100) not null,
`group` varchar(100) not null,
UNIQUE KEY(`id`),
PRIMARY KEY(`user`, `group`)
)
My model looks like this,
class Model_Groupuser extends ORM{
protected $_table_name = 'groupusers';
public function rules(){
return array(
'user' => array(
array('not_empty'),
array(array($this, 'user_group_not_exists')),
),
'group' => array(
array('not_empty'),
array(array($this, 'user_group_not_exists')),
)
);
}
public function user_group_not_exists($param){
// Need to get other field's value here.
}
}
Problem is every time user_group_not_exists is called, its called with a single parameter. Either user or group. But I need both to determine if the combination exists in the db already.
How can I get current model's fields' value?
You can get other fields value using $this->object() function.
public function user_group_not_exists($user_or_group){
$obj = $this->object();
$group = $obj['group'];
$user = $obj['user'];
// Check if ($group, $user) pair exists in db here
}
You have not really named your table columns comfortable. Naming them user and group and the relations also user and group creates ambiguity between the two.
As kohana does this great thing where you can access table fields, relationships etc. as if it's an objects property. $i_am_lazy = $object-><field,relation,whatever>. Now you named your fields and relations such that it is not clear what you are trying to get.
The only way you can access these id's now is like the following (or the hard way through $this->object() as stated in the other answer, both don't feel good anyway):
$user = $this->user->id;
$group = $this->group->id;
Though, I recommend just renaming the table columns.
create table `groupusers`(
`id` int not null auto_increment,
`user_id` varchar(100) not null,
`group_id` varchar(100) not null,
UNIQUE KEY(`id`),
PRIMARY KEY(`user`, `group`)
)
That way you can simply use $this->user_id or $this->group_id.

Creating a lookup field in a CakePHP form

I have a view created using Bake that has the following:
<fieldset>
<legend><?php echo __('Edit Device'); ?></legend>
<?php
echo $this->Form->input('DeviceID');
echo $this->Form->input('DeviceTypeID');
echo $this->Form->input('UserID');
echo $this->Form->input('Type');
echo $this->Form->input('KeyPadID');
echo $this->Form->input('Version');
echo $this->Form->input('Description');
echo $this->Form->input('UpdateID');
?>
</fieldset>
Which saves to the table:
CREATE TABLE `device` (
`DeviceID` VARCHAR(255) NOT NULL ,
`DeviceTypeID` INT(11) NOT NULL ,
`UserID` INT(10) NOT NULL ,
`Type` VARCHAR(10) NULL DEFAULT NULL ,
`KeyPadID` INT(10) NULL DEFAULT NULL ,
`Version` VARCHAR(255) NULL DEFAULT NULL ,
`Description` TINYBLOB NULL ,
`UpdateID` INT(11) NULL DEFAULT NULL ,
PRIMARY KEY (`DeviceID`),
INDEX `FK_USER` (`UserID`),
INDEX `FK_devices_updates` (`UpdateID`),
INDEX `FK_device_devicetype` (`DeviceTypeID`),
CONSTRAINT `FK_device_devicetype` FOREIGN KEY (`DeviceTypeID`) REFERENCES `devicetype` (`DeviceTypeID`),
CONSTRAINT `FK_devices_updates` FOREIGN KEY (`UpdateID`) REFERENCES `update` (`ID`) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT `FK_USER` FOREIGN KEY (`UserID`) REFERENCES `user` (`UserID`) ON UPDATE CASCADE ON DELETE CASCADE
)
My problem is that when the form is displayed, it shows DeviceTypeID and UserID as well as UpdateID as the foreign key value instead of a drop down with the caption being the text and the value being the ID column. How would I go about setting a field from the foreign table to be the display field and the id as being the value?
Update 11-02-2013
First of all I strongly suggest to convert your primary and foreign keys accordingly
so that they meet the CakePHP naming conventions.
This means that:
DeviceID should be id.
DeviceTypeID should be device_type_id
UserID should be user_id
Also all primary keys in your tables should be named as id.
This way you will never have to worry about anything, concerning your models etc.
After that, all your tables must be in plural form. This means that device table should be devices, so you should rename it also.
I assume that you also have the following tables: devices_types and users.
At this point, I should notice that I would prefer to have a table named devicetype. I avoid underscored names, because it's very easy to make mistakes using the correct form for each object, class etc. So I don't have to worry whether I should use the CamelCase or not.
Anyway
Your Device model should be something like that:
<?php
/** Device.php **/
class Device extends AppModel {
public $name = 'Device';
public $belongsTo = array(
'DeviceType' => array(
'className' => 'DeviceType',
'foreignKey' => 'device_type_id'
/** Specify other keys that meet your needs **/
),
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
};
?>
Also your DeviceType model should be similar to
<?php
/** DeviceType.php **/
class DeviceType extends AppModel {
public $name = 'DeviceType';
};
In your edit() method, you should query your DeviceType in something like this:
...
$devicetypes = $this->Device->DeviceType->find('list', array('id', 'caption'));
$this->set(compact('devicetypes'));
...
This way in your view the respective form element sets the <select> menu correctly.
PS: You should follow the CakePHP conventions about model-naming etc... Mine was just an example.

Categories