I have this schema:
propel:
autor:
id: ~
nombre: { type: varchar, size: 255, required: true }
libro:
id: ~
autor_id: { type: integer, size: 11, foreignTable: autor,
foreignReference: id}
titulo: { type: varchar, size: 255 }
paginas: { type: varchar, size: 255, required: true }
and this form class:
class AutorForm extends BaseAutorForm
{
public function configure()
{
$this->embedRelation('Libro');
}
public function foo()
{
$this->validatorSchema['Libro']['newLibro1']['paginas'] = new
sfValidatorPass();
return $this;
}
}
I'm calling foo() after the bind() (inside processForm()).
After submiting the Auto-Libro form, if I don't insert anything in the
field 'paginas' of the first embedded form (Libro), it shows
"Required".
But.. why if paginas has a validator-pass?
EDIT: after matt's answer, this is my code:
var_dump($this->embeddedForms['Libro']->validatorSchema['newLibro1']['paginas']);
$this->embeddedForms['Libro']->validatorSchema['newLibro1']['paginas'] = new sfValidatorPass(array('required' => false));
var_dump($this->embeddedForms['Libro']->validatorSchema['newLibro1']['paginas']);
It prints this:
object(sfValidatorString)[152]
protected 'requiredOptions' =>
array
empty
protected 'defaultMessages' =>
array
'required' => string 'Required.' (length=9)
'invalid' => string 'Invalid.' (length=8)
'max_length' => string '"%value%" is too long (%max_length% characters max).' (length=52)
'min_length' => string '"%value%" is too short (%min_length% characters min).' (length=53)
protected 'defaultOptions' =>
array
'required' => boolean true
'trim' => boolean false
'empty_value' => string '' (length=0)
'max_length' => null
'min_length' => null
protected 'messages' =>
array
'required' => string 'Required.' (length=9)
'invalid' => string 'Invalid.' (length=8)
'max_length' => string '"%value%" is too long (%max_length% characters max).' (length=52)
'min_length' => string '"%value%" is too short (%min_length% characters min).' (length=53)
protected 'options' =>
array
'required' => boolean true
'trim' => boolean false
'empty_value' => string '' (length=0)
'max_length' => int 255
'min_length' => null
object(sfValidatorPass)[196]
protected 'requiredOptions' =>
array
empty
protected 'defaultMessages' =>
array
'required' => string 'Required.' (length=9)
'invalid' => string 'Invalid.' (length=8)
protected 'defaultOptions' =>
array
'required' => boolean true
'trim' => boolean false
'empty_value' => null
protected 'messages' =>
array
'required' => string 'Required.' (length=9)
'invalid' => string 'Invalid.' (length=8)
protected 'options' =>
array
'required' => boolean false <<<<<<<<<<<< FALSE <<<<<<<<<<<<<<<<<<
'trim' => boolean false
'empty_value' => null
The problem is still the same: when i try to submit the form, the field 'paginas' is still required. Why?
sf 1.4/propel 1.6
Javi
Almost all validators, including the sfValidatorPass, default to required=true.
I believe the sfValidatorPass doesn't mean "it always passes validation" it means "pass the submitted value through without cleaning it or checking anything."
sfValidatorPass is an identity validator. It simply returns the value unmodified.
So I think you can just do
$this->validatorSchema['Libro']['newLibro1']['paginas'] = new
sfValidatorPass(array('required' => false));
Related
I have a form that allows a user to edit his information and upload an image. The image upload is optional but when I submit the form, I always have an error telling me that the image has not been uploaded.
Here is the partial fieldset code:
public function init() {
$this->add([
'name' => 'avatar',
'type' => 'file',
'attributes' => [
'id' => 'avatar',
'class' => 'input-file',
'accept' => 'image/*'
],
'options' => [
'label' => 'avatar',
]
]);
}
This fieldset implements InputFilterProviderInterface, so here is the code:
public function getInputFilterSpecification() {
return [
'avatar' => [
'type' => '\Zend\InputFilter\FileInput',
'allow_empty' => true,
'required' => false,
'validators' => [
[
'name' => 'FileExtension',
'options' => [
'extension' => ['jpg, jpeg, png'],
'message' => 'wrong_type_file'
]
],
[
'name' => 'FileSize',
'options' => [
'max' => '2MB',
'message' => 'file_too_large'
]
]
],
],
];
}
Despite the fact that this field is not required and allow_empty option is true, when I submit the form and debug the value, I still have validation that fails and this as my debug value:
'avatar' =>
array (size=5)
'name' => string '' (length=0)
'type' => string '' (length=0)
'tmp_name' => string '' (length=0)
'error' => int 4
'size' => int 0
Do you know how could I validate my form event when no file is downloaded?
Thanks in advance for your answers!
EDIT 1: Here are the messages returned by the form after submission:
'avatar' =>
array (size=2)
'fileExtensionNotFound' => string 'Extension d'image non admise (.jpg, .jpeg, .png seulement)'
'fileSizeNotFound' => string 'Le fichier est trop volumineux, 2097152 maximum.' (length=48)
EDIT 2: the action in the controller:
$prg = $this->fileprg($form);
if ($prg instanceof Response) {
return $prg;
} elseif (is_array($prg)) {
$data = $form->getData();
if ($form->isValid()) {
if ($data['person']['file']) {
// #TODO upload file
}
if (!$this->personMapper->updatePerson($data)) {
$this->flashMessenger()->addErrorMessage($this->translator()->translate('error_occurs_backup'));
} else {
$this->flashMessenger()->addSuccessMessage($this->translator()->translate('data_saved'));
}
} else {
// #TODO file error management
\var_dump('not valid');
\var_dump($form->getMessages());
}
}
I have the following tables/models: A, B, C, BC, D, BCD
(A:B 1:N) Connecting table D to BCD would be no problem. However I would like to filter key attributes as dropdown from A, B, C and D to find results in BCD (because in the end I need BCDid). In BCD next to BCid and Did I can store of course Aid, Bid and Cid, and it would seem to me quite an easy workaround, however I know it's totally against db normalisation. Is there another, better way (with eager loading of course)?
I've now this in BCD:
public function getB() {
return $this->hasOne(\app\models\B::className(), ['id' => 'Bid'])
->via('BC');
}
and it seems to work, but it's not eager loading.
And how do I get to model A? can I define it like this in BCD?:
public function getA() {
return $this->hasOne(\app\models\A::className(), ['id' => 'Aid'])
->via('BC')
->via(B);
}
It doesn't really work yet.
This way it works (BCSearch):
public function search($params) {
$query = BC::find()->joinWith('A', true)->joinWith('C', true);
Relation A in BC defined with "via". Dropdown filter also works.
But I still don't know how to achieve one more level deep into db structure.
This way it seems to work fully:
models/BCDSearch.php
public function search($params) {
$query = BCD::find()
->select([
'BCD.id',
'BCD.amount',
'A.id AS A_Id',
'A.name AS A_name',
'B.name AS B_name',
'B.name2 AS B_name2',
'C.name AS C_name',
'D.name AS D_name',
])
->leftJoin('BC', 'BC.id = BC_Id')
->leftJoin('B', 'B.id = B_Id')
->leftJoin('A', 'A.id = A_Id')
->leftJoin('C', 'C.id = C_Id')
->leftJoin('D', 'D.id = D_Id');
$query->andFilterWhere([
...
'A.id' => $this->A_Id,
'B.name' => $this->B_name,
'B.name2' => $this->B_name2,
'C.name' => $this->C_name,
'D.name' => $this->D_name,
]);
public function rules() {
return [
...
[[... 'A_Id', 'B_name', 'B_name2', 'C_name', 'D_name'], 'safe'],
];
}
models/base/BCD.php:
class BCD extends Whatever {
public $A_Id;
public $A_name;
public $B_name;
public $B_name2;
public $C_name;
public $D_name;
views/BCD/index.php:
GridView::widget([
'layout' => '{summary}{pager}{items}{pager}',
'dataProvider' => $dataProvider,
'pager' => [
'class' => yii\widgets\LinkPager::className(),
'firstPageLabel' => Yii::t('app', 'First'),
'lastPageLabel' => Yii::t('app', 'Last')],
'filterModel' => $searchModel,
'columns' => [
[
'attribute' => 'A_Id',
'value' => 'A_name',
'filter' => yii\helpers\ArrayHelper::map(app\models\A::find()->all(), 'id', 'name')
],
[
'attribute' => 'B_name',
'value' => 'B_name',
'filter' => yii\helpers\ArrayHelper::map([['id' => 'name1', 'name' => 'name1'], ['id' => 'name2', 'name' => 'name2']], 'id', 'name')
],
[
'attribute' => 'B_name2',
'value' => 'B_name2',
'filter' => yii\helpers\ArrayHelper::map([['id' => 'name2_1', 'name' => 'name2_1'], ['id' => 'name2_2', 'name' => 'name2_2']], 'id', 'name')
],
[
'attribute' => 'C_name',
'value' => 'C_name',
'filter' => yii\helpers\ArrayHelper::map(app\models\C::find()->all(), 'C_name', 'C_name')
],
[
'attribute' => 'D_name',
'value' => 'D_name',
'filter' => yii\helpers\ArrayHelper::map(app\models\D::find()->all(), 'D_name', 'D_name')
],
'amount',
...
hope this helps others. It was not easy to figure out (at least for me) this basically easy solution. I don't know why but so far I couldn't find any relevant info on the web like this.
I am having a little trouble adding validators to a ZF2 form object. I an building a form from a schema set in a database so I can validate a set of user input quickly.
This is the code that generates the form object
//initialise the form
$form = new Form();
//need to loop through the schema to create the form
for($i=0; $i < count($schema); $i++)
{
$form_options = array();
//add the basics to the form
$form_options['name'] = $schema[$i]['field_name'];
$form_options['type'] = $schema[$i]['field_type'];
//check if this is a required field
if($schema[$i]['is_required'] == 'true')
{
$form_options['required'] = true;
}
//add functions to filter the input form
$function_filters = explode(',', $schema[$i]['function_filter']);
if(!empty($function_filters))
{
$filters = array();
for($j=0; $j < count($function_filters); $j++)
{
$filters = array('name' => $function_filters[$j]);
}
$form_options['filters'] = $filters;
}
//add validators to the field array
$validators = array();
if(!is_null($schema[$i]['min_length']) && !is_null($schema[$i]['max_length']))
{
$validators[] = array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => (int) $schema[$i]['min_length'],
'max' => (int) $schema[$i]['max_length'],
)
);
}
//our regex validator if it exists
if(!is_null($schema[$i]['regex_filter']) || strlen($schema[$i]['regex_filter']) != 0)
{
$validators[] = array(
'name' => 'regex',
'options' => array(
'pattern' => $schema[$i]['regex_filter'],
'messages' => array(
\Zend\Validator\Regex::INVALID => $schema[$i]['regex_invalid'],
\Zend\Validator\Regex::NOT_MATCH => $schema[$i]['regex_not_match'],
\Zend\Validator\Regex::ERROROUS => $schema[$i]['regex_errorus'],
)
)
);
}
//only add the validators if theres something in there
if(!empty($validators))
{
$form_options['validators'] = $validators;
}
$form->add($form_options);
}
//return our form object
return $form;
The block of code behaves the way I expect it, the output of $form_option after it's performed the above code looks like this:-
Array
(
[name] => username
[type] => Text
[required] => 1
[filters] => Array
(
[name] => StripTags
)
[validators] => Array
(
[0] => Array
(
[name] => StringLength
[options] => Array
(
[encoding] => UTF-8
[min] => 3
[max] => 50
)
)
[1] => Array
(
[name] => regex
[options] => Array
(
[pattern] => /^[a-zA-Z0-9_]{3,50}$/
[messages] => Array
(
[regexInvalid] => L_REGEX_INVALID
[regexNotMatch] => L_REGEX_USERNAME
[regexErrorous] => L_REGEX_ERRORUS
)
)
)
)
)
When I came to test it, it ignores the StringLength & Regex validators entirely, and only pay attention to the required validator for the last form element.
Anyone have any idea what's gone wrong?
I been trying all day to do multiple insert for the same form based on a number and i couldn't go any where with it. I hope someone here would be help me out...
am using admin generator on 1.4 doctrine. I have a form which i generated with only two fields. what am trying to do is, based on a number inserted the form will be repeated x number of times.
In the generator file I added a partial which placed a text field in the beginning of the form with default value of 1. If I choose 2 the form below gets duplicated twice..
Here is what i did to my form.. In action
class prizesActions extends autoPrizesActions
{
public function executeNew(sfWebRequest $request)
{
$this->form = $this->configuration->getForm(null, array('n' => 5));
$this->prizes = $this->form->getObject();
}
}
and in the PrizesForm, I wrote the following
class PrizesForm extends BasePrizesForm
{
public function configure()
{
$array = $this->getOptions();
for ($i = 0; $i < $array['n']; $i++) {
$this->setWidgets(array(
'id' => new sfWidgetFormInputHidden(),
'prize_no' => new sfWidgetFormInputText(),
'prize' => new sfWidgetFormInputText(),
'created_at' => new sfWidgetFormDateTime(),
'updated_at' => new sfWidgetFormDateTime(),
));
$this->setValidators(array(
'id' => new sfValidatorDoctrineChoice(array('model' => $this->getModelName(), 'column' => 'id', 'required' => false)),
'prize_no' => new sfValidatorInteger(array('required' => false)),
'prize' => new sfValidatorString(array('max_length' => 200, 'required' => false)),
'created_at' => new sfValidatorDateTime(),
'updated_at' => new sfValidatorDateTime(),
));
$this->widgetSchema->setNameFormat('prizes['.$i.'][%s]');
$this->errorSchema = new sfValidatorErrorSchema($this->validatorSchema);
}
unset( $this['updated_at'],
$this['created_at']
);
}
}
I think the loop is working but its over writing the widgets at every entry and i cannot find other method to append instead. Any ideas?
Thanks,
Did you try embedForm()? Code below should give you an idea.
class PrizesForm extends BasePrizesForm
{
public function configure()
{
$this->setWidgets(array(
'id' => new sfWidgetFormInputHidden(),
'prize_no' => new sfWidgetFormInputText(),
'prize' => new sfWidgetFormInputText(),
'created_at' => new sfWidgetFormDateTime(),
'updated_at' => new sfWidgetFormDateTime(),
));
$this->setValidators(array(
'id' => new sfValidatorDoctrineChoice(array('model' => $this->getModelName(), 'column' => 'id', 'required' => false)),
'prize_no' => new sfValidatorInteger(array('required' => false)),
'prize' => new sfValidatorString(array('max_length' => 200, 'required' => false)),
'created_at' => new sfValidatorDateTime(),
'updated_at' => new sfValidatorDateTime(),
));
$this->widgetSchema->setNameFormat('prizes[%s]');
}
}
class PrizesGroupForm extends sfForm
{
public function configure()
{
$array = $this->getOptions();
for ($i = 0; $i < $array['n']; $i++)
{
$this->embedForm('prizes_' . $i, new PrizesForm());
}
$this->widgetSchema->setNameFormat('prizes_group[%s]');
}
}
I have an html table which presents working hours for projects
In the past 2 days I was trying to export this table to excel file. Finally i was able to do this by using an xml builder template.
Here is my file
xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
xml.Workbook({
'xmlns' => "urn:schemas-microsoft-com:office:spreadsheet",
'xmlns:o' => "urn:schemas-microsoft-com:office:office",
'xmlns:x' => "urn:schemas-microsoft-com:office:excel",
'xmlns:html' => "http://www.w3.org/TR/REC-html40",
'xmlns:ss' => "urn:schemas-microsoft-com:office:spreadsheet"
}) do
xml.Styles do
xml.Style 'ss:ID' => 'Default', 'ss:Name' =>'Normal' do
xml.Alignment 'ss:Vertical' => 'Bottom','ss:Horizontal' => 'Center'
xml.Borders
xml.Font 'ss:FontName' => 'Verdana'
xml.Interior
xml.NumberFormat
end
xml.Style 'ss:ID' => 'header' do
xml.Alignment 'ss:Vertical' => 'Bottom',
'ss:Horizontal' => 'Center'
xml.Font 'ss:FontName' => 'Arial','ss:Bold'=>'1'
xml.Interior 'ss:Color'=>'#99CCFF', 'ss:Pattern'=>'Solid'
end
end
xml.Worksheet 'ss:Name' => 'Projects Reports' do
xml.Table 'ss:DefaultColumnWidth'=>'100','ss:DefaultRowHeight' => '15' do
# Header
xml.Row 'ss:StyleID' => 'header' do
xml.Cell { xml.Data 'ID', 'ss:Type' => 'String' }
xml.Cell { xml.Data 'NAME', 'ss:Type' => 'String' }
xml.Cell { xml.Data 'Actual Hours', 'ss:Type' => 'String' }
xml.Cell { xml.Data 'Estimated Hours', 'ss:Type' => 'String' }
xml.Cell { xml.Data 'Deadline', 'ss:Type' => 'String' }
end
# Rows
for project in #projects
xml.Row do
xml.Cell { xml.Data project.id, 'ss:Type' => 'Number' }
xml.Cell { xml.Data project.name, 'ss:Type' => 'String' }
xml.Cell { xml.Data project.working_hours, 'ss:Type' => 'String' }
xml.Cell { xml.Data project.estimated_hours, 'ss:Type' => 'String' }
xml.Cell { xml.Data project.deadline, 'ss:Type' => 'String' }
end
end
end
end
end
In my original html view I display in the bottom of the table the sum of project working hours and I want that this property will be also displayed in the Excel file.
I did some checking in Google but didn’t find anything that can help me. I will appreciate if anyone can give me some direction to how I can write it in the xml builder file