Symfony - how to add embed Forms? - forms

I am trying to create a form Houses and embed the Images forms into it. I have follow the tutorial http://www.symfony-project.org/more-with-symfony/1_4/en/06-Advanced-Forms.
I have the following schema:
houses:
actAs: { Timestampable: ~ }
columns:
name: { type: string(255), notnull: true }
description: { type: string(5000), notnull: true }
images:
actAs: { Timestampable: ~ }
columns:
url: { type: string(255), notnull: true }
id_house: { type: integer, notnull: true }
relations:
houses: { local: id_house, foreign: id, foreignAlias: HousesImg}
and the code :
//lib/form/doctrine/ImagesCollectionForm
class ImagesCollectionForm extends sfForm
{
public function configure()
{
if(!$house= $this->getOption('house'))
{
throw new InvalidArgumentException('You must provide an house');
}
for ($i = 0; $i < $this->getOption('size',2); $i++)
{
$images = new images();
$images->house = $house;
$form = new imagesForm($images);
$this->embedForm($i, $form);
}
}
}
//lib/form/doctrine/housesForm.class.php
public function configure()
{
$form = new ImagesCollectionForm(null, array('house' => $this->getObject(),'size'=>2));
$this->embedForm('images', $form);
}
The fields are displayed as expected. But, when I press the save button I get a blank page and the data aren't saved in database.

use have not specified alias in Images relation with product
so by default symfony look it for relation name
so u need to change $images->house = $house; to $images->houses = $house;
or u can set alias in relation
hope this will help.

Related

Knex / Objection / PostgreSQL - Update of foreign key not working

I use Knex/Objection with PostgreSQL and I'm trying to update the foreign key but it returns null without any error.
Car model:
export class CarModel extends Model implements Car {
public static readonly tableName: string = 'cars';
public static readonly idColumn: string = 'car_id';
public static readonly jsonSchema: object = {
type: 'object',
properties: {
name: { type: 'string' },
carBrandId: { type: ['number', 'null'] },
lastUpdated: { type: ['string', 'null'], format: 'date-time' }
}
};
public static readonly relationMappings: RelationMappings = {
carBrandId: {
relation: Model.BelongsToOneRelation,
modelClass: 'brand.model',
join: {
from: 'brands.brand_id',
to: 'cars.fk_cars_brands'
}
}
};
public carId: number;
public name: string;
public carBrandId: number;
}
Creation of cars table:
import { Knex } from 'knex';
export function up(knex: Knex): Promise<boolean | void> {
return knex.schema.hasTable('cars').then(function(exists) {
if (!exists) {
return knex.schema.createTable('cars', (table) => {
table.specificType('car_id', 'INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY');
table.specificType('name', 'TEXT').notNullable();
table.specificType('car_brand_id', 'INTEGER').references('brand_id').inTable('brands').withKeyName('fk_cars_brands');
table.specificType('last_updated', 'TIMESTAMP');
});
}
})
}
export function down(knex: Knex): Knex.SchemaBuilder {
return knex.schema.dropTableIfExists('cars');
}
And then I use patch method in my service to update car_brand_id value from default to new added brand:
Database.getConnection().then((connection) => {
return CarModel.query(connection)
.patch({
carBrandId: 1, // row with brandId=1 exists in table brands!
lastUpdated: new Date().toISOString()
})
.where('carId', 10)
.returning('*')
.first()
});
And it returns the row where lastUpdated was updated, but carBrandId is still null:
{ carId: 10, name: 'Ferrari', carBrandId: null, lastUpdated: '<some date>' }
I would appreciate any help!

Validate DTO in controller when passing data to service

I am trying to insert a validation into PUT request(to update some data stored in MongoDB):
DTO:
export enum reportFields {
'startDate',
'targetDateOfCompletion',
'duration',
}
export class updateScheduleDto {
#IsOptional()
#IsString()
readonly frequency?: string;
#IsOptional()
#IsArray()
#IsEmail({}, { each: true })
#IsString({ each: true })
readonly emails?: string[];
#IsOptional()
#IsEnum(reportFields, { each: true })
#IsArray()
#IsString({ each: true })
readonly reportFields?: string[];
#IsOptional()
#Type(() => Number)
#IsNumber()
updatedAt?: number;
}
Controller:
#Put('reports/:id/schedule')
async updateScheduleData(
#Param('id') id: string,
#Body(new ValidationPipe()) updateData: updateScheduleDto,
) {
return this.reportService.updateScheduleData(id, updateData);
}
Service:
async updateScheduleData(id: string, updateData: updateScheduleDto) {
try {
updateData.updatedAt = this.utils.getCurrentTime();
const newData = await this.reportScheduleModel.findByIdAndUpdate(
id,
updateData,
{
new: true,
},
);
console.log(`Data has been updated to ${newData}`);
return newData;
} catch (error) {
throw new Error('>>>' + error);
}
}
But the validation not working over the keys. If I pass a non-valid key(like below) in the body object, even then the program executes without any error, how do I fix this? What am I missing?
{
"emaaaalls":["randomemail123#gmail.com"]
}
You need to pass the options { forbidUnknownValues: true } to the ValidationPipe. This will make class-validator throw an error when unknown values are passed in. You can read through the options here
You can make whitelist: true in ValidationPipe options.
When set to true, this will automatically remove non-whitelisted properties (those without any decorator in the validation class).
you can read more about this https://docs.nestjs.com/techniques/validation#stripping-properties

Angular 2. Set value of ControlGroup in data driven form

Let's say I have this model:
export class MyModel {
constructor(
public id: number,
public name: string
) {}
}
and this ControlGroup:
export class MyComponent {
form: ControlGroup;
model: MyModel;
constructor(builder: FormBuilder) {
this.form = this.builder({
'id' : [''],
'name' : ['']
})
}
}
To get form's data I can simply do that (if field names match):
this.model = this.form.value;
But how can I set form's value in the same manner?
something like: this.form.value = model;
Getting the following error:
Cannot set property value of #<AbstractControl> which has only a getter
Thank you!
UPD: Based on Günter Zöchbauer's suggestion below I ended up with that helper method:
setFormValues(form: ControlGroup, model: any) {
for(var key in model) {
var ctrl = (<Control>form.controls[key]);
if ( ctrl != undefined )
ctrl.updateValue(model[key]);
}
}
The ControlGroup returned from this.builder.group(...) doesn't support to set the value. To set the value you have to set it on each control individually like:
setValue() {
let value = {id: 'xxx', name: 'yyy'};
Object.keys(value).forEach((k) => {
this.form.controls[k].updateValue(value[k]);
});
}
Plunker example

Sequelize self association creates extra column

I have the following model defined with Sequelize:
module.exports = function (sequelize, DataTypes) {
var Genre = sequelize.define('Genre', {
name: {
type: DataTypes.STRING,
allowNull: false
}
}, {
classMethods: {
associate: function (models) {
Genre.hasMany(models.Genre, {as: 'subGenre'});
Genre.belongsTo(models.Genre, {as: 'parentGenre'});
}
}
});
return Genre;
}
The idea is that there will be parent genres, and each may have several sub-genres. When I run sync(), the table is created fine, but there is an extra column (GenreId) that I can't quite explain:
"id";"integer";
"name";"character varying";255
"createdAt";"timestamp with time zone";
"updatedAt";"timestamp with time zone";
"GenreId";"integer";
"parentGenreId";"integer";
The sensible way to interpret the model is to only have a parentGenreId column, but I am not sure how to define that bi-directional relationship (genre may have parent, genre may have many children) with only one column being added.
How can I rework the model to allow the relationship to be defined correctly?
I think you could use through (not tested)
module.exports = function (sequelize, DataTypes) {
var Genre = sequelize.define('Genre', {
name: {
type: DataTypes.STRING,
allowNull: false
}
}, {
classMethods: {
associate: function (models) {
Genre.hasMany(models.Genre, {as: 'children', foreignKey: 'ParentId'});
Genre.belongsTo(models.Genre, {as: 'parent', foreignKey: 'ParentId'});
}
}
});
return Genre;
}
So you could have Genre#getChilren and Genre#getParent
EDIT: Due to #mick-hansen, through applies for belongsToMany, not useful for this case. My fault. Corrected using his recommendation

saving embedded Propel symfony form for one-to-one relationship

I have a pair of tables that have a one-to-one relationship.
I have a complaint form that needs to embed a person form inside of that, the relevant schema is below:
complaint:
id: ~
created_at: ~
updated_at: ~
complainant_id: { type: integer, foreignTable: person_data, foreignReference: id, onDelete: setnull }
status: { type: tinyint, default: 1 }
complaint_title: { type: varchar(64) }
complaint_number: { type: varchar(16) }
recipient: { type: varchar(128) }
person_data:
id: ~
created_at: ~
updated_at: ~
company_name: { type: varchar(64) }
first_name: { type: varchar(64) }
last_name: { type: varchar(64) }
email: { type: varchar(128) }
I am able to successfully save both objects to the database but the main complaint object is not being updated with the complainant_id of the person_data row.
Does anyone know why this isn't working correctly and how to force it to update the complaint object correctly?
I am using symfony 1.4.13, Propel 1.6.3.
UPDATE:
Here is the code for the embedded form:
<?php
public function configure()
{
$use_fields = array();
// ...other fields added...
$sub_form = new PersonDataForm(array(), array());
$this->embedForm('complainant', $sub_form);
array_push($use_fields, 'complainant');
$this->useFields($use_fields);
}
I've found a solution to this problem.
Override the saveEmbeddedForms method in the form class.
Updating the main object occurs after the saving of the embedded forms so the ids are available to update the main object.
public function saveEmbeddedForms($con = null, $forms = null)
{
// save the embedded forms
parent::saveEmbeddedForms($con, $forms);
// loop through all embedded forms and update the main object with their ids
foreach($this->getEmbeddedForms() as $name => $embedded_form)
{
switch($name)
{
case 'recipient':
// criteria to determine if the sub-object should be saved or not
if($embedded_form->getObject()->getFirstName() == '' && $embedded_form->getObject()->getLastName() == '')
{
$embedded_form->getObject()->delete();
$this->getObject()->setRecipientId(null);
$this->getObject()->save();
}
else
$this->getObject()->setRecipientId($embedded_form->getObject()->getId());
break;
case 'complainant':
if($embedded_form->getObject()->getFirstName() == '' && $embedded_form->getObject()->getLastName() == '')
{
$embedded_form->getObject()->delete();
$this->getObject()->setComplainantId(null);
$this->getObject()->save();
}
else
{
$this->getObject()->setComplainantId($embedded_form->getObject()->getId());
}
break;
default:
break;
}
}
// save the main object with the new sub-object keys set
$this->getObject()->save();
}
Unfortunately there is nowhere that I can find on the internet with this explanation. So here it is for those that come after me.