I'm making a fairly large symfony3 application. It's my first one and I must say I'm pretty amazed by the robustness of the framework. It's an application that lets users create "events". And every event is happening in a certain "location". I have two bundles "EventBundle" and "VenueBundle". A venue can host many events so there's a one to many relation between the two. At this point I have an event creation page with a dropdown input that is automaticly filled with the venues already in the database. The EntityType field made it easy for me to implement that relation. This is an awesome symfony feature.
Because it's possible that not every venue is already in the database at the moment of creating a new event, I want to use a small "quick create venue" modal window (based on zurb-foundation) in the event creation wizard. It's to prevent users from exiting the wizard to add a new venue.
I was struggeling for the past two or three days or so to have two forms from different entities on one twig page. I have already found the answer in this question: Symfony - Two forms from two entities on the same page. But the answer raised a second question for me. I think it's easier to explain if I first show the code I'm having right now:
public function createAction(Request $request)
{
$event = new Event();
$eventForm = $this->createForm(EventType::class, $event);
$venue = new Venue();
$venueQuickForm = $this->createForm(VenueQuickType::class, $venue, array(
'action' => $this->generateUrl('massiv_venue_quickcreate')
));
$eventForm->handleRequest($request);
if($eventForm->isSubmitted() && $eventForm->isValid()) {
$event = $eventForm->getData();
$event->setPostedBy(1);
$event->setUpdatedBy(1);
$em = $this->getDoctrine()->getManager();
$em->persist($event);
$em->flush();
return $this->redirectToRoute('massiv_event_homepage');
}
$venueQuickForm->handleRequest($request);
if($venueQuickForm->isSubmitted() && $venueQuickForm->isValid()) {
$venue = $venueQuickForm->getData();
$venue->setPostedBy(1);
$venue->setUpdatedBy(1);
$em = $this->getDoctrine()->getManager();
$em->persist($venue);
$em->flush();
}
In the createForm method of the second form (venueQuickForm) I added the option "action" with a url pointing to the venue controller's quickcreate action. That was my idea, I had this already in my code before I found the answer, but kept that line to see how it would behave. It turns out it is ignored because in that quickCreateAction method I simply put a "ok" response and that page is not shown when a press the submit button. The rest of the code works fine, the venue is saved in the database.
So I am about to delete that line, but is the code above indeed the way to go? Intuitively I want to keep both codes seperate so putting the "save venue" part in the Venue controller seems locic to me or is that not the way Symfony is designed to work?
Related
I'm recently new Symfony (2.8) and I'm trying, that after an image file submission from a form, is to populate a second entity with the mains colors of the image.
For now, I just made a test from the controller like this:
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($image);
$extractor->setImage($image->getFile());
$palette = $extractor->extractPalette();
foreach($palette as $color => $weight){
$imageColor = new Color();
$imageColor->setImage($image);
$imageColor->setRgb($color);
$imageColor->setWeight($weight);
$em->persist($imageColor);
}
$em->flush();
It's working but I don't think that the colors should be in the form or the controller neither is the image entity.
So, how should I deal with the colors ?
I have two solutions in mind to save Colors outside your controller:
Create a Doctrine listener (on prePersist and preUpdate) that will generate Colors when an Image is persisted.
Before flush, dispatch a custom Event and catch it with an EventListener. When caught, execute method that populate Colors.
Advantage of solution 1: Colors will automatically be generated for an Image, whether you are in a Controller, a Command, or somewhere else.
Advantage of solution 2: You can decide more precisely when you want to generate Colors, by dispatching your custom event.
I've gotten stuck on something that I believe is down to my in-experience with the many to many relationship.
In my application I am mapping interests to products with a many to many. I wish to create the following scenario whereby the full list of interests is listed under a specific product with check boxes. For every check box that is selected when the form is submitted, a row is added to the InterestProductAssignment table.
In my product controller I call the full list of interests-
$interests = Interest::model()->findAll();
Actually I don't get a lot further than this as my mind is in knots wondering where to proceed from here. What I've tried so far is building an array of InterestProductAssignment objects to match the interests array I've returned above. I've tried passing it to the view and building out the form, but I've gotten myself fairly confused by trying to match the two up and I can't believe I'm using Yii correctly as it's messy.
Is anyone able to outline a solution for this problem that enables me to have a checkbox against every interest that would add a link between the product and interest upon submitting? I'd be interested to see what you'd write in the controller and view.
Just to clarify, this page is the subject of just one product.
EXTENSION
For the supplementary problem I'm having, I'm posting the code I've got, this may also help others implement a similar thing, once we find the mistake.
My relation within my product controller reads like this-
'interests'=>array(self::MANY_MANY, 'Interest', 'interest_product_assignment(interest_id, product_id)')
Controller
public function actionUpdate($id)
{
// loadModel as been adapted to be called "->with('interests')"
$model=$this->loadModel($id);
// Uncomment the following line if AJAX validation is needed
// $this->performAjaxValidation($model);
if(isset($_POST['Product']))
{
$model->attributes=$_POST['Product'];
if($model->save())
foreach($_POST['ProductInterest'] as $i => $interest_id){
$this_interest = new InterestProductAssignment;
$this_interest->product_id = $model->id;
$this_interest->interest_id = $interest_id;
$this_interest->save();
}
$this->redirect(array('view','id'=>$model->id));
}
$this->render('update',array(
'model'=>$model,
));
}
Relevant part of view _form.php
<div class="row">
<?php echo CHtml::checkBoxList('ProductInterest', CHtml::listData($model->interests, 'interest_id', true), CHtml::listData(Interest::model()->findAll(), 'id', 'interest'));?>
</div>
The problem is that the second field in checkBoxList does not seem to properly fill in the check boxes that are already selected. I suspect that the root cause of this is likely a daft mistake. I can't spot it though and I'm not familiar enough with checkBoxList.
Thanks in advance.
Goose,
I think what you are looking for here is the CheckBoxList.
In your form you can use the code
echo CHtml::checkBoxList('Product_Interests', $model->getInterests(), CHtml::listData(Interest::model()->findAll(), 'interest_id', 'interest_title'));
//$model->getInterests() is a method that should returns an array of IDs of all the currently selected interests
Then in your controller you can use the code:
foreach($_POST['Product_Interests'] as $interest_id=>$checked)
if($checked)
$model->addInterest($interest_id); //Add Interest
else
$model->removeInterest($interest_id); //Remove an Interest if it exists
//Note: If you have a model that connects these tables those can be created or destroyed here
I am new to the symfony framework. I've worked on an already existing project so I want change
a selectbox into a textbox using symfony 1.2.
Some more information and code snippets would have helped but..
There are several places this could be achieved, I will go for the 2 most direct and hopefully, common.
1)
Your form has already been customised and will likely live in plugins/yourPlugin/lib/form/yourForm.class.php
In here there will be something like $this->setWidget('my_input', new sfWidgetChoice(array([...])));
There are many "Choice" widgets it may be (Doctrine or Propel if they come from the database)
You will need to change it to $this->setWidget('my_input', new sfWidgetFormInput());
2)
Your form has not been modified before and is the generated form
You will need to go into your actions.class.php (or components.class.php) and find where it says $this->form = new yourForm();
Make a new form in plugins/yourPlugin/lib/form and make it extend your previous form.
class yourNewForm extends yourForm
Then add
public function setup()
{
$this->setWidget('my_input', new sfWidgetFormInput());
}
You will likely have to update the validator as well, or it will be expecting data in the "selct box" format.
http://www.symfony-project.org/api/1_1/sfWidgetFormInput
I have a custom class that populates a controller's action parameters based on the typehint of the parameter. This works well for documents (using public properties and setters).
My aim is to make the controller simple:
function updateAction(Article $article)
{
$dm = new DocumentManager(); // code elsewhere
$dm->merge($article);
$dm->flush();
return $this->redirect('/article/' . $article->getId());
}
The problem is that the input supplying the fields to programatically populate the Article class doesn't contain all of the properties of an Article class (perhaps the edit form only contains Title and Content, but disregards Author, etc).
I was hoping that the presence of an ID would allow the document to be merged gracefully with what is currently in the database. However, any fields that are missing at the time of a merge will be removed from the document in the database.
Is there a way to update a document in such a way that only the fields that are present (non-null, I guess) are updated?
Rather than hitting the db twice - once for the find, and once for the update, you can use a FIND_AND_UPDATE query.and do it all in one step.
See this docs page for details: http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/find-and-update.html
It seems that a clean way would be to bind the model AFTER retrieving it from the database. Something along the lines of ASP.NET MVC's UpdateModel.
function updateAction($id)
{
$dm = new DocumentManager(); // code elsewhere
$article = $dm->getRepository('Article')->find($id);
$this->updateModel($article);
$dm->flush();
return $this->redirect('/article/' . $article->getId());
}
If there are any better suggestions, feel free to answer...
How to create a multi-model form in Yii? I searched the entire documentation of Yii, but got no interesting results. Can some one give me some direction or thoughts about that? Any help will be appreciable.
In my expirience i got this solution to work and quickly understandable
You have two models for data you wish collect. Let's say Person and Vehicle.
Step 1 : Set up controller for entering form
In your controller create model objects:
public function actionCreate() {
$Person = new Person;
$Vehicle = new Vehicle;
//.. see step nr.3
$this->render('create',array(
'Person'=>$Person,
'Vehicle'=>$Vehicle)
);
}
Step 2 : Write your view file
//..define form
echo CHtml::activeTextField($Person,'name');
echo CHtml::activeTextField($Person,'address');
// other fields..
echo CHtml::activeTextField($Vehicle,'type');
echo CHtml::activeTextField($Vehicle,'number');
//..enter other fields and end form
put some labels and design in your view ;)
Step 3 : Write controller on $_POST action
and now go back to your controller and write funcionality for POST action
if (isset($_POST['Person']) && isset($_POST['Vehicle'])) {
$Person = $_POST['Person']; //dont forget to sanitize values
$Vehicle = $_POST['Vehicle']; //dont forget to sanitize values
/*
Do $Person->save() and $Vehicle->save() separately
OR
use Transaction module to save both (or save none on error)
http://www.yiiframework.com/doc/guide/1.1/en/database.dao#using-transactions
*/
}
else {
Yii::app()->user->setFlash('error','You must enter both data for Person and Vehicle');
// or just skip `else` block and put some form error box in the view file
}
You can find some examples in these two Yii wiki articles:
Yii 1.1: How to use a single form to collect data for two or more models?
Yii 1.1: How to use single form to collect data for two or more models (CActiveForm and Ajax Validation edition).
You don`t need a multi-model. The right use of the MVC pattern requires a Model that reflects your UI.
To solve it, you'll have to use a CFormModel instead of an ActiveRecord to pass the data from View to Controller. Then inside your Controller you`ll parse the model, the CFormModel one, and use the ActiveRecord classes (more than one) to save in database.
Forms Overview and Form Model chapters in Yii Definitive Guide contains some details and samples.
Another suggestions -
Also we can use Wizard Behavior, It's an extension that simplifies the handling of multi-step forms. In which we can use multi model forms for registration process flow or others.
Demo - http://wizard-behavior.pbm-webdev.co.uk/