Techniques: ORM, Doctrine 1.1.6, KohanaPHP
With Doctrine 1.1.6. How do I spread a model over different tables?
Detailed situation:
I have the class Entity which contains an ID, login and password and has one emailaddress, many addresses and some other relations. I have two other classes, Company and Person, which extend Entity. I want to extend them using Column aggregation so all login and password information is saved in one place. Now I want to add specific columns to my Person class (firstname, lastname, etc), but I can't find how to do this. The only example the documentation gives is one without extra columns.
Current classes
Entity class:
class Entity extends Doctrine_Record
{
public function setTableDefinition() {
$this->setTableName('entity');
$this->hasColumn('id', 'integer', 4, array(
'type' => 'integer',
'length' => 4,
'unsigned' => 0,
'primary' => true,
'autoincrement' => true,
));
$this->hasColumn('login', 'string', 64, array(
'type' => 'string',
'length' => 64,
'fixed' => false,
'primary' => false,
'notnull' => true,
'autoincrement' => false,
));
$this->hasColumn('password', 'string', 64, array(
'type' => 'string',
'length' => 64,
'fixed' => false,
'primary' => false,
'notnull' => true,
'autoincrement' => false,
));
$this->hasColumn('created', 'date', null, array(
'type' => 'date',
'primary' => false,
'notnull' => false,
'autoincrement' => false,
));
$this->hasColumn('modified', 'date', null, array(
'type' => 'date',
'primary' => false,
'notnull' => false,
'autoincrement' => false,
));
$this->setSubclasses(array(
'Person' => array("type" => 1)
));
}
}
Person Class:
class Person extends Entity
{
public function setTableDefinition() {
$this->setTableName('person');
$this->hasColumn('id', 'integer', 4, array(
'type' => 'integer',
'length' => 4,
'unsigned' => 0,
'primary' => true,
'autoincrement' => true,
));
$this->hasColumn('firstname', 'string', 255, array(
'type' => 'string',
'length' => 255,
'fixed' => false,
'primary' => false,
'notnull' => true,
'autoincrement' => false,
));
$this->hasColumn('insertion', 'string', 64, array(
'type' => 'string',
'length' => 64,
'fixed' => false,
'primary' => false,
'notnull' => false,
'autoincrement' => false,
));
$this->hasColumn('lastname', 'string', 255, array(
'type' => 'string',
'length' => 255,
'fixed' => false,
'primary' => false,
'notnull' => true,
'autoincrement' => false,
));
}
}
SQL generated:
CREATE TABLE `person` (
`id` INT AUTO_INCREMENT,
`firstname` VARCHAR(255) NOT NULL,
`insertion` VARCHAR(64),
`lastname` VARCHAR(255) NOT NULL,
PRIMARY KEY(`id`)
) ENGINE = INNODB
CREATE TABLE `entity` (`
id` INT AUTO_INCREMENT,
`login` VARCHAR(64) NOT NULL,
`password` VARCHAR(64) NOT NULL,
`created` DATE,
`modified` DATE,
PRIMARY KEY(`id`)
) ENGINE = INNODB
Can somebody tell me how to accomplish this?
You'll have to add these columns to the entity class since everything is basically stored in the same table. That means that these columns will be available to the company entries too, but maybe you can forbid using them there.
You can however use different tables and reference them with a foreign key. This will give you a layout like this:
entity - stores basic information common to all entities. Furthermore you store the type of this entity (User, Company) as an id.
entity_types - stores the coreesponding table for each entity type
User - stores information specific to the users and a key to the corresponding entity.
Company - same as User, may be nearly empty if there is no additional info (depending on how you implement this solution, you can still add one row just containing the entity id for simplicity)
This way you can alway (lazy) fetch additional information about your entities and the table itself remains slim. If you realize entity as an column aggregation Doctrine will take care of returning the right object. Then you can add your custom functions for fetching the additional information.
You can leave out the indirection in 2.
Related
I have a question how to remove validation from LastName inside client address edit. I need to allow numbers inside this field.
I found here thread Prestashop : Remove Lastname Field Rules Validation From B.O, but this solution is not working.
Finally, I have caught the issue. You are editing in admin panel and I was sharing code for front end. Please try below steps for admin:
Step 1 - file classes/Address.php
'lastname' => ['type' => self::TYPE_STRING, 'validate' => 'isAnything', 'required' => true, 'size' => 255],
Change this to isAnything
Step 2 - src\PrestaShopBundle\Form\Admin\Sell\Address/CustomerAddressType.php
Change your code to below code:
line 209: add('last_name', TextType::class, [
'label' => $this->trans('Last name', 'Admin.Global'),
'help' => $genericInvalidCharsMessage,
'required' => true,
'constraints' => [
new NotBlank([
'message' => $this->trans(
'This field cannot be empty.', 'Admin.Notifications.Error'
),
]),
new CleanHtml(),
new TypedRegex([
'type' => TypedRegex::TYPE_GENERIC_NAME,
]),
new Length([
'max' => AddressConstraint::MAX_LAST_NAME_LENGTH,
'maxMessage' => $this->trans(
'This field cannot be longer than %limit% characters',
'Admin.Notifications.Error',
['%limit%' => AddressConstraint::MAX_LAST_NAME_LENGTH]
),
]),
],
])
Now, you are ready to go and check.
Go to the file classes/Address.php file:
'lastname' =>array('type' => self::TYPE_STRING, 'validate' => 'isCustomerName', 'required' => true, 'size' => 32),
to :
'lastname' =>array('type' => self::TYPE_STRING, 'validate' => 'isAnything', 'required' => true, 'size' => 32),
validate to isAnything.
I think you were modifying in customer class. Please try with Address.php.
Thanks for sharing the files.
I have resolved the case. You need to modify the classes/form/CustomerAddressForm.php
line 229
$isValid &= $this->validateField('lastname', 'isName', $this->translator->trans(
'Invalid name',
[],
'Shop.Forms.Errors'
));
Change to:
$isValid &= $this->validateField('lastname', 'isAnything', $this->translator->trans(
'Invalid name',
[],
'Shop.Forms.Errors'
));
I want to do this good with override. I have an issue with override this class. I have created module to override but it is not working. There is a way to override this without editing core files?
services:
_defaults:
public: true
form.type.customer_address:
class: 'Playdev\PrestaShopBundle\Form\Admin\Sell\Address\CustomCustomerAddressType'
public: true
arguments:
- '#prestashop.adapter.form.choice_provider.country_state_by_id'
- '#=service("prestashop.adapter.legacy.context").getContext().country.id'
- '#router'
tags:
- { name: form.type }
https://ibb.co/VVjnJYr
There is a file class override:
\modules\pd_overridemodule\src\PrestaShopBundle\Form\Admin\Sell\Address\CustomCustomerAddressType.php
https://ibb.co/7QPHrqx
And I have an error when I am inside Edit Address Form Backoffice
Type error: Too few arguments to function PrestaShopBundle\Form\Admin\Sell\Address\CustomerAddressType::__construct(), 0 passed in C:\laragon\www\prestabiolab\vendor\symfony\symfony\src\Symfony\Component\Form\FormRegistry.php on line 92 and exactly 5 expected
[Symfony\Component\Debug\Exception\FatalThrowableError 0]
https://ibb.co/YfwhtKq
I have found a solution
Need to create module and call hookactionCustomerAddressFormBuilderModifier.
public function hookactionCustomerAddressFormBuilderModifier(array $params)
{
/** #var $formBuilder \Symfony\Component\Form */
$formBuilder = $params['form_builder'];
// remove lastname field
$formBuilder->remove('last_name');
// get all fields without removed
$allFields = $formBuilder->all();
// remove all fields
foreach ($allFields as $inputField => $input) {
$formBuilder->remove($inputField);
}
foreach ($allFields as $inputField => $input) {
// normally add fields
$formBuilder->add($input);
// add fields after firstname
if ($inputField == 'first_name') {
$formBuilder->add('last_name', TextType::class, [
'label' => $this->trans('Last name', [], 'Admin.Global'),
'help' => $this->trans(
'Invalid characters:',
[],
'Admin.Notifications.Info'
) . ' ' . TypedRegexValidator::GENERIC_NAME_CHARS,
'required' => true,
'constraints' => [
new NotBlank([
'message' => $this->trans(
'This field cannot be empty.', [], 'Admin.Notifications.Error'
),
]),
new CleanHtml(),
new TypedRegex([
'type' => TypedRegex::TYPE_GENERIC_NAME,
]),
new Length([
'max' => AddressConstraint::MAX_LAST_NAME_LENGTH,
'maxMessage' => $this->trans(
'This field cannot be longer than %limit% characters',
['%limit%' => AddressConstraint::MAX_LAST_NAME_LENGTH],
'Admin.Notifications.Error',
),
]),
],
]);
}
}
}
Now I think it works okey with override :)
I try to call a setting form, which shows input forms for saving data into price database.
My model throws the above Exception during rendering:
Unknown Property – yii\base\UnknownPropertyException
Setting unknown property: yii\validators\NumberValidator::0
error in line of _price-item:
$form->field($model, "[{$i}]credits")->textInput(['maxlength' => 8])
Model:
<?php
namespace app\models;
use Yii;
/**
* #package app\models
*
* #property integer $id
* #property integer $credits
* #property integer $price
* #property integer $reduced_price
* #property integer $discount
* #property string $start
* #property string $end
* #property integer $active
*/
class Price extends \app\base\ActiveRecord
{
public function rules()
{
return [
[['credits'], 'integer', 'required'],
[['price'], 'integer','integerOnly' => false,'required', 'min' => 0, 'max' => 10000],
[['reduced_price','discount'],'integer','integerOnly' => false,'min' => 0, 'max' => 10000],
[['start','end'],'format' => 'php:Y-m-d H:i:s'],
[['active'], 'integer'],
[['active'], 'in', 'range' => array_keys(self::$_CONDITIONS)],
];
}
}
Widget:
<?php DynamicFormWidget::begin([
'widgetContainer' => 'wrapper-prices',
'widgetBody' => '.container-items',
'widgetItem' => '.item',
'limit' => 30,
'min' => 1,
'insertButton' => '.add-item',
'deleteButton' => '.remove-item',
'model' => count($prices) ? $prices[0] : new \app\models\Price(),
'template' => $this->render('_price-item', [
'i' => 0,
'form' => $form,
'model' => count($prices) ? $prices[0] : new \app\models\Price(),
]),
'formId' => 'dynamic-form',
'formFields' => [
'credits',
'price',
'reduced_price',
'discount',
'start',
'end',
'active',
],
]); ?>
mysql:
CREATE TABLE `price` (
`id` int(11) NOT NULL,
`credits` int(11) NOT NULL,
`price` float NOT NULL,
`reduced_price` float DEFAULT NULL,
`discount` float DEFAULT NULL,
`start` datetime DEFAULT NULL,
`end` datetime DEFAULT NULL,
`active` smallint(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Can anybody tell me, what is wrong ?
my head almost burns
It's your first 2 rules
[['credits'], 'integer', 'required'],
[['price'], 'integer','integerOnly' => false,'required', 'min' => 0, 'max' => 10000],
You are setting 2 core validators integer , required in one rule which is wrong. The integer for example validator takes max or min parameters and that too as an associative array 'min'=>10,and assigns the property values like $obj->min=10, and your code would force the integer validator to interpret 'required' as 0=>'required' , which clearly explains the error above.
Unknown Property – yii\base\UnknownPropertyException
Setting unknown property: yii\validators\NumberValidator::0
Chang you rules method to
public function rules()
{
return [
[['credits','price'], 'required'],
[['price'], 'integer','integerOnly' => false, 'min' => 0, 'max' => 10000],
[['reduced_price','discount'],'integer','integerOnly' => false,'min' => 0, 'max' => 10000],
[['start','end'],'datetime','format' => 'php:Y-m-d H:i:s'],
[['active','credits'], 'integer'],
[['active'], 'in', 'range' => array_keys(self::$_CONDITIONS)],
];
}
Update
Your 4th rule will also be throwing error should be
[['start','end'],'datetime','format' => 'php:Y-m-d H:i:s'],
I have updated the code block above too.
the correct working rule is:
Thanks to Muhammad
public function rules()
{
return [
[['credits','price'], 'required'],
[['price','reduced_price','discount'],'integer','integerOnly' => false,'min' => 0, 'max' => 10000],
[['start','end'],'datetime','format' => 'php:Y-m-d'],
[['status','credits'], 'integer'],
[['status'], 'in', 'range' => array_keys(self::$_CONDITIONS)],
];
}
I'm building a simple app using F3 and Cortex but I'm running in some problems trying to create a simple Many to Many relation. Its just two tables (and a third, from the relation), the tables already exist and have some data:
class User extends \DB\Cortex {
protected
$fieldConf = array(
'name' => array (
'type' => 'VARCHAR128',
'nullable' => false,
'required' => true,
),
'email' => array (
'type' => 'VARCHAR256',
'nullable' => false,
'required' => true,
),
'password' => array (
'type' => 'VARCHAR128',
'nullable' => false,
'required' => true,
),
// 'groups' => array(
// 'has-many' => array('Group', 'group_id', 'user_has_group')
// )
),
$db = 'db',
$table = 'user';
}
and
class Group extends \DB\Cortex {
protected
$fieldConf = array(
'name' => array(
'type' => 'VARCHAR128',
'nullable' => false,
'required' => true,
),
'description' => array(
'type' => 'VARCHAR128',
'nullable' => false,
'required' => true,
),
// 'users' => array(
// 'has-many' => array('User', 'user_id', 'user_has_group')
// )
),
$db = 'db',
$table = 'group';
}
accourind with this model of the database
But if I remove those comment marks, I get a strange error while acessing anything:
Internal Server Error
No valid DB Connection given.
And on refresh:
Internal Server Error
Fatal error: Leaked 1 hashtable iterators
It looks silly, but I searched all over and spent the better part of a whole day, and still can't seem to make it work.
I'm using fuelphp's orm to model my data. How can I control which order my child elements are returned when doing a cascaded find.
For example, here's the example config to attach comments to a post:
protected static $_has_many = array(
'comments' => array(
'key_from' => 'id',
'model_to' => 'Model_Comment',
'key_to' => 'post_id',
'cascade_save' => true,
'cascade_delete' => false,
)
);
How can I say, for example, order the comments by the 'date_entered' field?
Thanks in advance,
David
You can add the order_by clause to the conditions.
protected static $_has_many = array(
'comments' => array(
'key_from' => 'id',
'model_to' => 'Model_Comment',
'key_to' => 'post_id',
'cascade_save' => true,
'cascade_delete' => false,
'conditions' => array(
'order_by' => array(
'field1' => 'DESC',
'field2' => 'ASC',
)
),
),
);
Note that as they are defined in the relation, they are always active and can not be turned off!
When i try to save data to my model Doctrine throws this exception:
Message: Couldn't get last insert identifier.
My table setup code is:
$this->hasColumn('id', 'integer', 4, array(
'type' => 'integer',
'length' => 4,
'fixed' => false,
'unsigned' => false,
'primary' => true,
'autoincrement' => true,
));
Please help. Thanks.
Check to make sure that the column in your database is set up as auto_increment. It looks like the Doctrine class is handling it as an auto_increment but it's not set up as such in the DB.
For me, the problem was the default paramater.
$this->hasColumn('inscription_id', 'integer', 4, array(
'type' => 'integer',
'length' => 4,
'fixed' => false,
'unsigned' => false,
'primary' => true,
// 'default' => '0', !!! get "couldn't get last inserted identifier doctrine"
'notnull' => true,
'autoincrement' => true,
));
This worked for me:
$this->hasColumn('cd_fabricante', 'integer', 4, array(
'type' => 'integer',
'length' => 4,
'unsigned' => true,
'primary' => true,
'auto_increment' => true,
) );
Had the same parameters as you previously, same error too.
EDIT: I recently found about adding "auto_increment" to the PK column definition and now I it behaves the same as any given ID field handled by Doctrine with whatever name I choose