datepicker posts now() even if empty ZF2 Doctrine2 - doctrine-orm

I have a form with two dates where I use bootstrap3 datepicker. The dates are not mandatory. When I submit the form and one or both of the dates are not filled out, the posted entity contains the actual datetime instead of beeing NULL. When I check the $_POST the dates are NULL.
I use doctrine2 and ZF2. Maybe you need more informations... Thanks in advance for any help.

Solution for me was to add filter Zend\Filter\Null to the form element so that empty strings were converted to NULL.
array(
'name' => 'Zend\Filter\Null',
'options' => array(
'type' => 'string',
),
),
Cause of the problem:
If your form is set to BindOnValidate then a hydrator will be used, which can manipulate the values. It converts strings to DateTime objects if the entity property is a DateTime. Test this using
$form->setBindOnValidate(false);
Which should mean that your value remains an empty string '' immediately after calling isValid().
I found that DoctrineObject hydrator->handleTypeConversions() was converting any string (e.g. from my form POST) into a DateTime object for consistency with the entity field. An empty string results in a DateTime representing time() i.e. current time.
When I intercepted the form POST and converted empty string '' to null, the hydrator left that field as null.
I'm not sure whether it is best to manipulate the POST data within the controller before validation, or to modify the hydration strategy but I presumably either could work?
EDIT: I tried adding a custom strategy to the DoctrineObject hydrator to convert empty string '' to NULL but the DoctrineObject does not trigger the custom strategy, it does the type conversion irrespective.
Copy of the DoctrineObject hydrator code that was making the change:
protected function handleTypeConversions($value, $typeOfField)
{
switch($typeOfField) {
case 'datetime':
case 'time':
case 'date':
if (is_int($value)) {
$dateTime = new DateTime();
$dateTime->setTimestamp($value);
$value = $dateTime;
} elseif (is_string($value)) {
$value = new DateTime($value);
}
break;
default:
}
return $value;
}

You should define the date fields with a default value of NULL and then add the nullable=true option.
/**
* #ORM\Column(type="date", nullable=true)
*/
private $someDateField= null;
Take a look at the documentation for more details.

I discovered something new:
In my controller, the $form->isValid() function is the cause that the date fields are populated with the current date time.
I wrote out the fields values just BEFORE the isValid() and they were NULL as expected, because not filled out in the form.
I wrote out the fields values just AFTER the isValid() and they were NOT NULL, but an object.
The code is quite simple:
if ($form->isValid()) {
$objectManager->persist($group);
$objectManager->flush();
}
I really don't know ghow to solve this...

Related

How to make attribute_names list all attribute names in a document with dynamic attributes

I have a Rails 4.2 application with mongoid in which I'm importing csv files with test results. I can't define all fields in the model because they change from test to test and theres always around 700 of them. I use Dynamic Attributes and importing and displaying works fine.
I'm trying to use attribute_names method to get all attribute names but all I get is those defined in the model. If I don't define anything in the model it comes back with "_id" only. attributes method on the other hand can see attributes in the actual document on the other hand.
>> #results.first.attributes.count
=> 763
>> #results.first.attribute_names
=> ["_id"]
I also tried fields.keys, same problem
>> #results.first.fields.keys
=> ["_id"]
My model at the moment looks like this
class Result
include Mongoid::Document
include Mongoid::Attributes::Dynamic
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
Result.create! row.to_hash
end
end
end
Can somebody explain how to make it work?
Any help greatly appreciated.
This part is not very clear in the documentation.
and this answer doesn't address how you can make your case works ( I really don't know)... but it has one monkey patch at the end...
all I know is why this case not working...
as the documentation states
When dealing with dynamic attributes the following rules apply:
If the attribute exists in the document, Mongoid will provide you with your standard getter and setter methods.
For example, consider a person who has an attribute of "gender" set on the document:
# Set the person's gender to male.
person[:gender] = "Male"
person.gender = "Male"
# Get the person's gender.
person.gender
this is not your case... cause as it appears you are not defining any attributes in your model...
what applies in your case (from the code you showed and problem you described)
If the attribute does not already exist on the document,
Mongoid will not provide you with the getters and setters and will enforce normal method_missing behavior.
In this case you must use the other provided accessor methods: ([] and []=) or (read_attribute and write_attribute).
# Raise a NoMethodError if value isn't set.
person.gender
person.gender = "Male"
# Retrieve a dynamic field safely.
person[:gender]
person.read_attribute(:gender)
# Write a dynamic field safely.
person[:gender] = "Male"
person.write_attribute(:gender, "Male")
as you can see... there is no way for mongoid to add the setter and getter methods in runtime...
Monkey Patch
you could add a field (maybe string, array, hash, whatever suites you) to the document (attribute exists in the document)
on populating the document from the CSV row.. just save what are the fields of the CSV in that field... (hold the CSV keys in it)
use your predefined field (that holds the keys) instead of using .keys.
code example in your case.
class Result
include Mongoid::Document
include Mongoid::Attributes::Dynamic
field :the_field_that_holds_the_keys, type: Array
# ...
end
and in your controller:
#results.first.some_attribute
#=> method missing error
#results.first[:some_attribute]
#=> some_value
#results.first.the_field_that_holds_the_keys
#=> [:some_attribute, :some_other_attribute, :yada]

ZF2 + DoctrineORMModule + Boolean field

I'm setting up a form through annotations using
/**
* #ORM\Column(type="boolean", nullable=false, name="is_public")
* #Annotation\Required(false)
* #Annotation\AllowEmpty()
* #Annotation\Attributes({"placeholder":"Is Public"})
* #Annotation\Filter({"name":"boolean"})
* #Annotation\Options({"label":"Is Public"})
*/
private $isPublic;
This form is built using the doctrine annotation builder and the doctrine entity hydrator. The entity is then bound to this form. There is an issue when passing a boolean field, in that any value is treated as false, except 1, passing 0 results in an error message of "cannot be empty".
Can somebody please advise as to how I can use boolean fields properly using this method? Ideally I'd like to be able to use the filter before the field is validated? Not only that, but the validation is ignoring the AllowEmpty() and Required(false) fields.
Kind Regards,
ise
What you say is happening seems correct. Checkbox on forms submit nothing when unchecked, but the hydrator obviously needs to be able to know when the user intends to clear the value (mark false).
Pretty sure hydrator should work with empty string for false too. Required and AllowEmpty don't really make sense with a Boolean, especially in your case, because you also put nullable=false
This way I solved it $form->getInputFilter()->get('isPublic')->setContinueIfEmpty(true); just before $form->isValid()

Doctrine and ZF2

I am facing trouble using doctrine join. I can't share my code. But I can tell you scenario.
Please help me to achieve that.
I have created 2 entity. One User and Language.
User table is having foreign key language_id. and Language is master table with id and code fields.
I want to fetch user with some criteria, such a way it returns Language code from Language table as well.
I write join for that but it returns some full object...
Not sure how to fetch corresponding language code from Language table for language_id set in user table
If there is some example you know which can help me then also fine
i have return this in __construct()
$this->languageObj = new ArrayCollection();
when we print it is gives this
[languageObj:User:private] => Common\User\Entity\Language Object
(
[languageId:Language:private] => 1
[languageCode:Language:private] => en
[languageName:Language:private] => English
[languageCode2:Language:private] => User Object
RECURSION
)
I am not able to fetch languageCode from the object
You need methods defined in your entity to return the value from the object. It seems like everything is correct you would just need to grab the value from the entity. Here is an example:
$userEntity->getLanguageObj()->getLanguageId();
Your user Entity would need the getLanguageObj method which you can define like this:
public function getLanguageObj() {
return $this->languageObj;
}
And your Language Entity would also need a getLanguageId method:
public function getLanguageId() {
return $this->languageId;
}
Hope that helps!

symfony 1.4 and propel - foreign constraints fails saved relation

I am working in a Symfony 1.4 project with Propel 1.4.2.
I have 2 related tables. workshop and trainers which is a many to many relation mapped by a join table (workshop_trainers) which contains the workshop_id and the trainer_id).
In my Workshop Form I have a select box for adding the trainers to the workshop. The problem is when the workshop is new (Create) I get an error:
Cannot add or update a child row: a foreign key constraint fails
This happens because, when saving the workshop_trainers relation the workshop_id field is null. IsnĀ“t Propel intelligent enough to know that there is a relation between the tables and save the base object first? What I am doing wrong?
My trainer list widget.
$this->widgetSchema['workshop_trainer_list'] = new sfWidgetFormChoice(array(
'choices' => $trainers,
'multiple' => true,
));
Thanks for your help.
This is not fixing the problem but that's the easiest practical solution to this problem:
In the form, simply deactivate the workshop_trainer_list field if the object is a new one (doesn't have an ID yet).
Something like:
if ($this->getObject()->isNew())
{
$this->offsetUnset('workshop_trainer_list'); // not sure of that method name
}
A better solution is to update the doSave method to have the ID first, something like this:
protected function doSave($con = null)
{
$isNew = $this->getObject()->isNew();
if (null === $con)
{
$con = $this->getConnection();
}
// retrieve the value of workshop_trainer_list here and remove it from the form
$trainers = ...
$this->offsetUnset('workshop_trainer_list');
// save without it
parent::doSave($con);
// add it back
$this->getObject()->set...
// save
$this->getObject()->save($con);
}

Dropdown list in yii from other controller

I want list all objects of my model, and write to file the id of selected model. Using SiteController I render my page but what model I shall use?
$models = myModel::model()->findAll();
$list = CHtml::listData($models, 'id', 'name');
echo CHtml::dropDownList( ???? , $select, $list);
If I get what you're trying to do, You're talking about two models. Like tbl_product => Product and tbl_category => Category.
For demonstration purpose: Say, you want to create a new product and every product must belong to a category, then you might make use of the active dropdown. Using code similar to yours, you can say:
$category = Category::model()->findAll();
$list = CHtml::listData($category, 'id', 'name');
An important thing to note is that CHtml::activeDropDownList() expects different kinds of arguments. The main difference between it and CHtml::dropDownList() is that activeDropDownList( is tied to a Model while dropDownList() isn't.
public static string activeDropDownList(CModel $model, string $attribute, array $data, array $htmlOptions=array ())
public static string dropDownList(string $name, string $select, array $data, array $htmlOptions=array ())
So, using the example, assuming our Product model has a field called category_id, then the dropdown list would be generated using either:
CHtml::activeDropDownList($model, 'category_id', $list);
or if you've created an Activeform object like this:
$form=$this->beginWidget('CActiveForm');
then you could create the dropDown list like this:
$form->dropDownList($model, 'category_id', $list);
Where $model would be the Product model.
I hope this has been helpful.