Simple date with Sonata Admin Bundle - postgresql

I have a form with Sonata Admin Bundle with a date, to set the birthday of the user we want to add. Here goes MemberAdmin.php :
/**
* #param \Sonata\AdminBundle\Form\FormMapper $formMapper
*
* #return void
*/
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('General')
->add('username')
->add('name')
->add('surname')
->add('birthdate', 'birthday', array('format' => 'yyyy-MM-dd'))
// ...
}
And my problem is when I send the form, I obtain Error: Call to a member function format() on a non-object ... But if I do print_r($birthdate) in the Entity class it shows me the DateTime object ...
Here are the interesting Entity parts:
/**
* #var date
*
* #ORM\Column(name="birthdate", type="date", nullable=true, options={"default" = "1990-01-01 00:00:00"})
* #Assert\DateTime()
*/
private $birthdate;
/**
* Set birthdate
*
* #param \DateTime $birthdate
* #return Membre
*/
public function setBirthdate($birthdate)
{
$this->birthdate = $birthdate;
return $this;
}
/**
* Get birthdate
*
* #return \DateTime
*/
public function getBirthdate()
{
return $this->birthdate;
}
My problem, currently, is that I don't know what I should do, I just want the date, no time, no anything else, i don't know if the column should be date (I work with PostgreSQL). What should I use for the types of my variables, I feel lost here, no simple Date possible ??
I tried to figure out from where it could come, but when I change too much I end up with: This form should not contain extra fields directly in the form, or even Incorrect value, but the field is a valid date ...
Thanks for your help !!

Change your field type to sonata_type_date_picker and test if the error message persist.
->add('birthdate', 'sonata_type_date_picker', array(
'format' => 'dd/MM/yyyy',
'widget' => 'single_text',
'label' => 'Birthdate',
))

From manual (sonata-project.org) :
If no type is set, the Admin class will use the one set in the
doctrine mapping definition.
So, you can try this:
->add('birthdate', null, array('format' => 'yyyy-MM-dd'));

#wr0ng.name you should never overwrite vendor code. NEVER.
There is something wrong with your mapping somewhere. You can use doctrine's commands to check your entity.

Edit
As #rande said, modifying vendors files is not the way to go, it provided an easy temp workaround for a local private app. As it is not dedicated to stay like that, I took care of the issue once I had more time. Sorry for the delay to come back to you guys.
I played around, tried with multiple setups, it took me time to figure it out, but I finally came to the conclusion that the issue... was caused by another date, that I was generating wrong in the constructor one line above.
Also, thanks to all of you, that guided me on the right path!

Related

ExtbaseObject with relation to multiple occurences of the same object

I've got an ExtbaseObject with a relation that can contain multiple references to the same subobject.
I've extended the mm table for the relation with a uid field and set the option MM_hasUidField in the tca. In the backend everything works as intended:
But if I load the object in the frontend I get only one occurence of "Testzusatzpaket 1":
The tca configuration in question is:
'zusatzpakete' => [
'label' => 'LLL:EXT:ned_beratung/Resources/Private/Language/locallang_db.xlf:tx_nedberatung_domain_model_beratung.zusatzpakete',
'config' => [
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
'foreign_table' => 'tx_nedshop_domain_model_artikel',
'multiple' => true,
'maxitems' => 99,
'MM' => 'tx_nedberatung_beratung_zusatzpakete_mm',
'MM_hasUidField' => true,
],
],
In the model, the object is defined as an object storage:
/**
* zusatzpakete
*
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\NED\NedShop\Domain\Model\Artikel>
* #cascade remove
*/
protected $zusatzpakete = null;
/**
* Adds a Zusatzpaket
*
* #param \NED\NedShop\Domain\Model\Artikel $zusatzpakete
* #return void
*/
public function addZusatzpakete(\NED\NedShop\Domain\Model\Artikel $zusatzpakete)
{
$this->zusatzpakete->attach($zusatzpakete);
}
/**
* Removes a Zusatzpakete
*
* #param \NED\NedShop\Domain\Model\Artikel $zusatzpaketeToRemove The Zusatzpakete to be removed
* #return void
*/
public function removeZusatzpakete(\NED\NedShop\Domain\Model\Artikel $zusatzpaketeToRemove)
{
$this->zusatzpakete->detach($zusatzpaketeToRemove);
}
/**
* Returns the Zusatzpakete
*
* #return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\NED\NedShop\Domain\Model\Artikel> $zusatzpakete
*/
public function getZusatzpakete()
{
return $this->zusatzpakete;
}
/**
* Sets the Zusatzpakete
*
* #param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\NED\NedShop\Domain\Model\Artikel> $zusatzpakete
* #return void
*/
public function setZusatzpakete(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $zusatzpakete)
{
$this->zusatzpakete = $zusatzpakete;
}
Why does this not work in the frontend, what am I missing?
That's the way the Extbase ObjectStorage works. It retrieves the object hash of each object and uses this to only store objects uniquely. And the Extbase persistence ensures that every object is only created once by storing it in the persistence session after retrieval.
One option to bypass this is to promote your MM table to a 1st level table including TCA and an own domain model. This way each relation is a separate object and thus unique which allows you to have multiple relations to the same Artikel. And example of such a promoted MM table is the sys_file_reference table in TYPO3.
Your Beratung model would then have a multi-valued property (ObjectStorage) typed to the newly introduced relation model. That model then needs a property to represent the related Artikel.
If you do this you can still keep the field names in your MM table if you want to continue maintaining it with the current setup. Then you only need to change your model relations as described. However if you want to have more descriptive names in your table, you can rename the fields but then it cannot be managed as MM table in TYPO3 anymore so you'd need to change your base table field type e.g. to inline.
One final suggestion: it is general consensus to use English for everything in code including model and property names. This makes it a lot easier for others to get into your code and blends well with the English of the programming language.

handle request of incomplete missing fields symfony form

I have created a small Symfony (Sf3.2 + php7) web with a Task Entity. I have my controller where I have a new action to create a new task. Everything is fine. Now, some of the form fields are not necessary to be filled (created date, for example). So I have removed from my taskType:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add->('name')->add('description');
}
Now my form looks exactly as I want. The surprise is that when I submit it my controller pass it to doctrine,and every missing field is write as a NULL. NO!, I don't want that, what I wanted is my default mysql value.
Ok, I read the doc. There seems to be two ways of handling data:
$form->handleRequest($request);
$form->submit($request->request->get($form->getName()));
I've found in the API, (not in the doc) that submit have a second parameter, a boolean:
API says:
bool $clearMissing Whether to set fields to NULL when they are missing in the submitted data.
Great! this is exactly what I need. Lets see it:
public function newAction(Request $request) {
$task = new Task();
$form = $this->createForm('myBundle\Form\TaskType', $task);
$form->submit($request->request->get($form->getName()), false);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($task);
$em->flush();
$response = new Response();
$response->setContent(json_encode(array(
'data' => 'success',
)));
$response->headers->set('Content-Type', 'application/json');
return $response;
return $this->redirectToRoute('task_success');
}
return $this->render('task/new.html.twig', array(
'task' => $task,
'form' => $form->createView(),
));
}
Well, after tried everything I always get NULL in all missing fields. What am I doing wrong.
Thank you, Caterina.
I'm afraid there must be something more. And I was thinking that perhaps is not Doctrine the solution. The Symfony log shows me the insert and it's setting a Null in every empty field, on empty fields and missing fields. In fields like status i could set a default value, at doctrine level, but if I want to set created_at field, I suppose that must be Mysql the responsible is setting current timeStamp.
Anyway this is my ORM property status from Task Entity.
/**
* #var \scrumBundle\Entity\Taskstatus
*
* #ORM\ManyToOne(targetEntity="scrumBundle\Entity\Taskstatus")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="status", referencedColumnName="id")
* })
* #ORM\Column(name="status", type="integer", nullable=false,options={"default":1})
*/
private $status;
And this is the property id from TaskStatus:
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false, options={"default":1})
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
As you see I tried to follow your advise of setting the option default value. I've tried to create to set the form with handleRequest and submit with same result
$task = new Task();
$form = $this->createForm('scrumBundle\Form\TaskType', $task);
//$form->handleRequest($request);
$form->submit($request->request->get($form->getName()), false);
if ($form->isSubmitted() && $form->isValid()) {
I even tried to debug step by step submit function, a real Hell, because submit is a recursive function to much complex for me.
Ok , again thank for you time.
Regards, Cate
PS. Sorry about my poor english.;^)
Short answer - when you persist a PHP object through Doctrine, you must have absolutely every value set that you want. If your PHP object has null fields, Doctrine will manually set them as null in your entity. Doctrine doesn't assume that just because a value is null in your PHP object, that you don't want it included on your INSERT statement. This is because null is a perfectly valid SQL value.
So, whatever your PHP object is at the time of insert is exactly what is going to be inserted into your database, null values and all.
When you are editing an entry, Doctrine will only update the fields that are different, so this isn't a concern. Your concern is when persisting entities.
The easiest solution is to copy your MySQL default value into your PHP entity. Like one of these many ways:
// set default value on the variable itself
public $someVar = 100;
// set default values in the constructor (so when creating a new entry)
public function __construct()
{
$this->createdAt = new \DateTime();
$this->isActive = true;
}
You can also use Lifecycle Callbacks to set when inserting a new entry:
/**
* #ORM\Entity()
* #ORM\HasLifecycleCallbacks()
*/
class Task
{
// ...
/**
* #ORM\PrePersist
*/
public function setCreatedAtValue()
{
$this->createdAt = new \DateTime();
}
}

Unable to pass a specific form in the view

I have created a CRUD with Symfony 3 that allows me to create different missions with a few specificities. I want to to create a function that allows someone with a specific role to change a mission's status just by clicking a button, that would be shown in the view like this
{{form_start(missionInProgress) }}
<input type="submit" value="Submit" />
{{form_end(missionInProgress) }}
Since I'm a real newbie and I can't find concrete example on Google, I tried a lot of things, but none worked so far. I tried to create a public function that would modify the mission's status when someone clicks on the input button
public function that updates the mission's status:
/**
* #Route("/{id}/encours", name="mission_encours")
* #Security has_role('ROLE_RECRUTEUR')
* #Method("POST")
*/
public function enCoursAction(Request $request, Mission $mission){
$form = $this->missionInProgress($mission);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$em = $this->getDoctrine()->getManager();
$mission->setStatut("En cours");
$em->persist($mission);
}
}
And I also tried to create a private function like the one that allows a mission to be deleted from anywhere.
**Private function that calls the public function: **
/**
* #param Mission $mission
* #Security has_role('ROLE_RECRUTEUR')
* #return \Symfony\Component\Form\Form The form
*/
private function missionInProgress(Mission $mission){
$this->createFormBuilder()
->setAction($this->generateUrl('mission_encours', array('id' => $mission->getId())))
->setMethod('POST')
->getForm();
}
Following the "createDeleteForm" example, I implemented it in the showAction as follow:
/**
* Finds and displays a Mission entity.
*
* #Route("/{id}", name="mission_show")
* #Method("GET")
*/
public function showAction(Mission $mission)
{
$deleteForm = $this->createDeleteForm($mission);
$enCours = $this->missionInProgress($mission); /* There */
return $this->render('mission/show.html.twig', array(
'mission' => $mission,
'delete_form' => $deleteForm->createView(),
'missionInProgress' => $enCours->createView(), /* And there */
));
}
But when I try to see the result, I get the following error:
Error: Call to a member function createView() on null
Obviously nothing gets inside missionInProgress(), but I can't figure why and how to make this damn thing work. I also don't think that everything I did was necessary, but I thought that if I do this, I might increase my success chances...
Anyone has an idea ?
Thank you in advance
Try to add returnin your missionInProgress() method

Symfony using DiscriminatorColumn with formbuilder

I have a user entity:
/**
* Class User
* #package Somepackage
* #ORM\Entity(repositoryClass="Somepackage\UserBundle\Entity\Repository\UserRepository")
* #ORM\Table(name="user")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"usereins" = "UserEins", "userzwei" = "UserZwei", "admin" = "Admin"})
*/
class User implements UserInterface, \Serializable {
Now I want to create a form with formbuilder, where a new user can be created and the "discr" can be selected via dropdown. But if I try this:
$builder
->add('discr', 'choice', array( ... ), 'required' => true ));
then Symfony says:
Neither the property "discr" nor one of the methods "getDiscr()", "isDiscr()", "hasDiscr()", "__get()" exist
How can I do this? Is it possible? I have been googling for over an hour and I cannot seem to find anything except other stackoverflow questions no one has been able to answer.
Cerads answer is correct, thank you very much. I added another field to my table in which I store the type of user, so I didn't have to worry about the discr field.

An error occurred while trying to call Controller->createAction()

I am trying to create something with extbase, but the error-message I get is not very helpful. I took the blog_example extension as a guide. A (maybe) important difference is: I don't have a database table because I want to write a custom domain repository that connects to an external servive through REST.
The actual error message (displayed above the plugin, not as an exception message):
An error occurred while trying to call Tx_MyExt_Controller_SubscriptionController->createAction()
Classes/Controller/SubscriptionController:
Stripped down to the important parts.
class Tx_MyExt_Controller_SubscriptionController extends Tx_Extbase_MVC_Controller_ActionController
{
/**
* #var Tx_MyExt_Domain_Repository_SubscriberRepository
*/
protected $subscriberRepository;
/**
* #return void
*/
public function initializeAction()
{
$this->subscriberRepository = t3lib_div::makeInstance('Tx_MyExt_Domain_Repository_SubscriberRepository');
}
/**
* #param Tx_MyExt_Domain_Model_Subscriber $subscriber
* #dontvalidate $subscriber
* #return string The rendered view
*/
public function newAction(Tx_MyExt_Domain_Model_Subscriber $subscriber = null)
{
$this->view->assign('subscriber', $subscriber);
}
/**
* #param Tx_MyExt_Domain_Model_Subscriber $subscriber
* #return string The rendered view
*/
public function createAction(Tx_MyExt_Domain_Model_Subscriber $subscriber)
{ }
}
Classes/Domain/Model/Subscriber
class Tx_MyExt_Domain_Model_Subscriber extends Tx_Extbase_DomainObject_AbstractEntity
{
/**
* #var string
* #dontvalidate
*/
protected $email = '';
/**
* #param string $email
* #return void
*/
public function setEmail($email)
{
$this->email = $email;
}
/**
* #return string
*/
public function getEmail()
{
return $this->email;
}
}
Resources/Private/Templates/Subscription/new
<f:form action="create" controller="Subscription" objectName="Subscriber" object="{subscriber}" method="post">
<f:form.textfield property="email"></f:form.textfield>
<f:form.submit value="submit"></f:form.submit>
</f:form>
Facts
Adding $subscriber = null removes the message. But $subscriber is null then
A var_dump($this->request->getArguments()); displays the form's fields
There is an index action, and it is also the first action defined in ext_localconf.php
The hints and solutions I found aren't working for me, so I hope someone can guide me into the right direction.
I've got the same bug.
If you pass an Model as argument to an method, it will also validate the model fields.
I've had this annotation on my model property:
/**
*
* #var \string
* #validate NotEmpty
*/
It validates the "#validate" annotation.
The field in the database was empty so i got the error message
An error occurred while trying to call ...
It would be good if there was a better error message.
You need to customize the validation annotation or verify that the property is not empty in the database
Hope it helps somebody
In addtion: check any Validations in your Model and your TCA. If a field is marked as #validate NotEmpty in your Model and is not marked appropriately in the TCA, a record can be saved ignoring the #validate settings in the Model. This can happen if you change the Model and/or TCA after creating records.
An example:
Field 'textfield' is set to not validate, both in the TCA and the Model. You create a new record and save it without filling in the field 'textfield' (you can, it is not set to validate). You then change the Model setting 'textfield' to #validate NotEmpty and then try to show the record on the FE, you will get the error.
The solution for that example:
Simply remove the validation in your Model OR check validations in the TCA and Model so that they work together.
--
A German blog post covers this solution: http://www.constantinmedia.com/2014/04/typo3-extbase-an-error-occurred-while-trying-to-call-anyaction/
just override the template method getErrorFlashMessage in yout controller to provide a custom error message...
/**
* A template method for displaying custom error flash messages, or to
* display no flash message at all on errors. Override this to customize
* the flash message in your action controller.
*
* #return string|boolean The flash message or FALSE if no flash message should be set
* #api
*/
protected function getErrorFlashMessage() {
return 'An error occurred while trying to call ' . get_class($this) . '->' . $this->actionMethodName . '()';
}
classic case of "start over from scratch and it works, and if you compare it you have the same code, though".
I updated the code in the question, maybe it helps someone.