Having trouble adding checkbox values to db via orm
Working for normal fields but not if more than one checkbox is selected on the checkbox questions that allow more than one option
Here is the Form bit
<?php echo Form::label('first_name', 'First Name')?><br />
<?php echo Form::input('first_name', $profile->first_name, array('class'=>'inputbox')); ?><br />
<?php echo Form::label('last_name', 'Last Name')?><br />
<?php echo Form::input('last_name', $profile->last_name, array('class'=>'inputbox')); ?><br />
Favorite Genres:
<label><input type="checkbox" value="Horror" name="genres[]" />
<strong>Horror</strong></label><br />
<label><input type="checkbox" value="Thriller" name="genres[]" />
<strong>Thriller</strong></label><br />
Here is the controller bit
if ($_POST) {
if ($profile->values($_POST)->check()) {
$profile->user_id = $user;
$profile->save();
}
}
And Here is the Model bit
protected $_rules = array(
'first_name' => array(
'not_empty' => NULL,
),
'last_name' => array(
'not_empty' => NULL,
),
);
Only not working when more than one checkbox is selected, I get this error
Database_Exception [ 1241 ]: Operand should contain 1 column(s)
Not sure best approach for this.. Should I serialize or implode ? Where to do this?
I want to build basic search form in future to search 'like' values using this column.
This is an DB exception, not a Validation. You should use ORM relations for this situation:
class Model_Profile extends ORM {
protected $_has_many = array(
// profile has and belongs to many genres
'genres' => array(
'through' => 'profiles_genres', // pivot table name
),
);
}
And then change genres for current profile, like this:
try
{
$profile->check();
$profile->save();
// now replace old genres with a new list
// clear genres
$profile->remove('genres');
foreach($this->request->post('genres') as $genre)
{
$genre = ORM::factory('genre')->where('name', '=', $genre);
if ($genre->loaded())
{
$profile->add('genres', $genre);
}
}
}
catch (Validate_Exception)
{
// wrong input
}
Note that not genres are separated table, not a profiles column. Also you will need a pivot table profiles_genres with profile_id and genre_id columns.
You should create a genres table and model and create a pivot table for genres and profiles:
CREATE TABLE IF NOT EXISTS `genres` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `genres_profiles` (
`profile_id` int(10) UNSIGNED NOT NULL,
`genre_id` int(10) UNSIGNED NOT NULL,
PRIMARY KEY (`profile_id`,`genre_id`),
KEY `fk_genre_id` (`genre_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Note that you don't need to create ORM model for pivot table.
You can find more in the official docs
Related
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.
I have two models: one for "Experts" (based on a database table of their contact details), and one for "Expertise" (such as 'PHP', 'JavaScript', 'Java', ..., also in database table). Since an expert can have more than one expertise, and an expertise can be held by several experts, this is a many-to-many relationship that is defined in the model classes, see below. The relationship should be stored in a junction table.
// in models/RcccExperts.php
class RcccExperts extends \yii\db\ActiveRecord
{
...
public function getRcccExpertise()
{
return $this->hasMany(RcccExpertise::className(), ['id' => 'expertise_id'])
->viaTable('rccc_experts_expertise', ['expert_id' => 'id']);
}
}
// in models/RcccExpertise.php
class RcccExpertise extends \yii\db\ActiveRecord
{
...
public function getRcccExperts()
{
return $this->hasMany(RcccExperts::className(), ['id' => 'expert_id'])
->viaTable('rccc_experts_expertise', ['expertise_id' => 'id']);
}
}
The user can add a new entry to the experts database via a form that contains data from both models. The user can assign expertise to the expert via a multiple select form field (implemented with select2 tags), where s/he can select from existing expertise (already in the database) or add new expertise tags.
When I try to link the two models to populate the junction table (that contains the expert IDs and expertise IDs), I get the following error message:
PHP Fatal Error – yii\base\ErrorException
Call to a member function getIsNewRecord() on array
in /Users/Sites/Yii/vendor/yiisoft/yii2/db/BaseActiveRecord.php at line 1248
I'm aware that when linking two models using ->viaTable, both models mustn't be newly created. In my case, the Expert model has just been saved and has a primary key id; I'm retrieving the Expertise ids that were selected for the Expert from the database.
Whichever way I try to link the two models, the problem seems to be that the Expertise is not recognized as an existing model, but as an array().
What am I doing wrong?!
More details on the error below:
* This parameter is only meaningful for a relationship involving a junction table
* (i.e., a relation set with [[ActiveRelationTrait::via()]] or [[ActiveQuery::viaTable()]].)
* #throws InvalidCallException if the method is unable to link two models.
*/
public function link($name, $model, $extraColumns = [])
{
$relation = $this->getRelation($name);
if ($relation->via !== null) {
if ($this->getIsNewRecord() || $model->getIsNewRecord()) {
throw new InvalidCallException('Unable to link models: the models being linked cannot be newly created.');
}
if (is_array($relation->via)) {
/* #var $viaRelation ActiveQuery */
list($viaName, $viaRelation) = $relation->via;
$viaClass = $viaRelation->modelClass;
// unset $viaName so that it can be reloaded to reflect the change
unset($this->_related[$viaName]);
} else {
...
After the form to create a new expert has been submitted, this is the Controller code to execute:
// in controllers/ExpertsController.php
/**
* ExpertsController implements the CRUD actions for RcccExperts model.
*/
class ExpertsController extends Controller
{
...
public function actionCreate()
{
$model = new RcccExperts();
$expertise = new RcccExpertise();
if ($model->load(Yii::$app->request->post()) && $expertise->load(Yii::$app->request->post())) {
if ($model->validate() && $expertise->validate()) {
if ($model->save()) {
// Once the Expert's model data has been saved
// Go ahead and process the Expertise ids form the multiple select form field:
// First save the expertise (coming from a multiple select form field where the user can
// select existing expertise tags or add new ones) to the database
$array = $expertise->expertise;
$expertise_ids = array();
foreach ($array as $key => $value) {
$exp = new RcccExpertise();
// Check that the expertise does not exist yet
$check1 = $expertise->find()->where(['id' => $value])->one();
$check2 = $expertise->find()->where(['expertise' => $value])->one();
if ($check1 == null && $check2 == null) {
$exp->expertise = $value;
// Save new expertise
$exp->save();
$result = $expertise->find()->select('id')->where(['expertise' => $value])->one();
$expertise_ids[] = $result->id;
}
else $expertise_ids[] = $value;
}
$expertise->id = $expertise_ids;
// Put the new expertise IDs in a model instance
$expertises = RcccExpertise::find()->where(['id' => $expertise->id])->all();
// Link expert model with expertise model
// to populate the junction table
$model->link('rcccExpertise', $expertises);
}
return $this->redirect(['view', 'id' => $model->id]);
}
}
else {
return $this->render('create', [
'model' => $model,
'expertise' => $expertise,
'languages' => $languages,
'attachments' => $attachments
]);
}
}
The MySQL tables holding this information look like this:
`CREATE TABLE `rccc_experts` (
`id` int(10) UNSIGNED NOT NULL,
`name` varchar(200) NOT NULL,
`email` varchar(100) NOT NULL,
`phone` varchar(100) NOT NULL,
`skype` varchar(100) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `rccc_experts`ADD PRIMARY KEY (`id`);
ALTER TABLE `rccc_experts` MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;COMMIT;
CREATE TABLE `rccc_expertise` (
`id` int(10) UNSIGNED NOT NULL,
`expertise` varchar(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `rccc_expertise` ADD PRIMARY KEY (`id`);
ALTER TABLE `rccc_expertise` MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;COMMIT;
CREATE TABLE `rccc_experts_expertise` (
`id` int(10) UNSIGNED NOT NULL,
`expert_id` int(10) UNSIGNED NOT NULL,
`expertise_id` int(10) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `rccc_experts_expertise` ADD PRIMARY KEY (`id`);
ALTER TABLE `rccc_experts_expertise` MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT;COMMIT;
Relation hasMany returns array of objects. To make it work, you have to loop over this array, like:
foreach ($model->rcccExperts as $expert) {
// do your stuff here on $expert variable
}
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.
I have a requirement where I need to build the relations between more than 3 tables.
I have 4 tables namely, Message, Flat, Person, Mapping tables.
Now, below tables have the following fields:
Message:
`Id` int(11) NOT NULL AUTO_INCREMENT,
`Mapid` int(11) DEFAULT NULL,
PRIMARY KEY (`Id`),
KEY `FK41715B218022FC0` (`MapId`)
Mapping
`Id` int(11) NOT NULL AUTO_INCREMENT,
`FlatId` int(11) DEFAULT NULL,
PRIMARY KEY (`Id`),
KEY `FKE2B3C68A24F94F50` (`FlatId`),
Flat
`Id` int(11) NOT NULL AUTO_INCREMENT,
`PersonId` int(11) DEFAULT NULL,
PRIMARY KEY (`Id`),
KEY `FK2FFF79122B94A6` (`PersonId`),
Person
`Id` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(255) DEFAULT NULL,
`FlatId` int(11) DEFAULT NULL,
`Phone` varchar(255) DEFAULT NULL,
PRIMARY KEY (`Id`),
KEY `FKC4E39B55AF5432C` (`FlatId`),
Now, I have to build relations in such a way that in the Cgridview(admin.php) of Message, i should display PersonId of flat table and Name and Phone of Person table along with the columns of Message table.
I have defined relations like this in model class of message(message.php)
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'mapping' => array(self::BELONGS_TO, 'Mapping', 'MapId'),
'flat'=>array(self::HAS_ONE,'Flat',array('FlatId'=>'Id'),'through'=>'mapping'),
'person'=>array(self::HAS_ONE,'Person',array('PersonId'=>'Id'),'through'=>'flat'),
);
}
Can anyone explain me the step by step procedure to display the columns of person table in message gridview.
With assuming you can create dataProvider for the CGridView:
<?php
$this->widget('zii.widgets.grid.CGridView',array(
'id'=>'message-grid',
'dataProvider'=>$yourDataProvider //such as $model->search();
'filter'=>$model,
'columns'=>array(
'Id',
'Mapid',
'person.name',
'person.FlatId',
'person.Phone',
));
?>
You can pass a DataProvider to the view and use it in CgridView widget or use a 'search()' action from $model.
You can personalize the relation columns like this:
<?php
$this->widget('zii.widgets.grid.CGridView',array(
'id'=>'messagePerson-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'Id',
'Mapid',
array(
'header'=>'Person Name', // Personalize column name
'value'=>'$data->flat->person->Name',
'htmlOptions'=>array('style'=>'width:10%;'), // Personalize html attributes
),
'flat.person.FlatId', // Or directly with default relation name.
'flat.person.Phone',
));
?>
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.