I would like to modify two forms in one page. I generated a module with Doctrine. I have:
public function executeEdit(sfWebRequest $request)
{
$this->forward404Unless($news = Doctrine_Core::getTable('News')->find(array($request->getParameter('news_id'))), sprintf('Object news does not exist (%s).', $request->getParameter('news_id')));
$this->form = new NewsForm($news);
}
this works fine.
I added:
public function executeEdit(sfWebRequest $request)
{
$this->forward404Unless($news = Doctrine_Core::getTable('News')->find(array($request->getParameter('news_id'))), sprintf('Object news does not exist (%s).', $request->getParameter('news_id')));
$this->form = new NewsForm($news);
$this->forward404Unless($other = Doctrine_Core::getTable('Other')->findByNewsId(array($request->getParameter('other_id'))), sprintf('Object other does not exist (%s).', $request->getParameter('other_id')));
$this->form = new OtherForm($other);
}
and I get this error:
500 | Internal Server Error | sfException The "OtherForm" form only
accepts a "Other" object.
I use findByNewsId()
This works ok - if I make foreach then I have these objects, but I can't show this in the Form.
How can I achieve this?
It sounds (roughly) like you're looking to edit multiple objects within a single form - so in your example, multiple "Other" items (your question is currently slightly unclear). If so, you'll probably want to take a look at Symfony's embedded forms functionality - see this tutorial for details.
Related
Hi i am working on a update method for updating a profile, right now i am updating the profile by passing the model as a parameter but i am wanting to pass in the id of a profile so the route is patch('/profiles/podcast/{id}'' i am quite new to laravel so im wondering how do i modify the controller and phpunit test to update this way when grabbing objects in laravel?
Update function in the controller:
public function update(PodcastProfile $podcastProfile)
{
$this->user = Auth::user();
$this->podcastProfile = $podcastProfile;
if (!$this->hasPodcastProfile()) {
abort(400, "You don't have a podcast profile configured");
}
$this->validation();
$this->transaction();
return $this->podcastProfile->toJson();
}
This is the current route for the update method
Route::patch('/profiles/podcast/{podcastProfile}', 'PodcastProfileController#update');
This is the phpunit test case for the function
/**
* #test
*/
public function it_should_update_podcast_profile()
{
$podcastDetails = $this->handlePostRequestToController();
$this->json('patch', '/profiles/podcast/' . $podcastDetails['id'], $this->updateData)
->assertSuccessful();
$this->checkPodcastProfile($this->updateData, $podcastDetails['id']);
$this->checkGuestFormats($this->updateData['guest_format']);
$this->checkAvailability($this->updateData['availability']);
$this->checkEquipment($this->updateData['equipment']);
$this->checkCategories($this->updateData['categories']);
$this->checkLocation($this->updateData['country'], $this->updateData['city']);
}
Hej,
if I understand your question correctly you would just pass the id of that profile instead, just like you said:
Route::patch('/profiles/podcast/{podcastProfileId}','PodcastProfileController#update');
and then fetch the profile by that given id.
so something like:
$this->podcastProfile = App\PodcastProfile::find($podcastProfileId)
Also i feel like choosing the route-model-binding approach like #Remul described would be the better approach.
I am trying to make a car rental agency, and in the backend I want to be able to set the rates. However there are two different (but related) ways to do this, either by individual date, or in bulk, by selecting a date range and looping over the individual dates.
In the controller I have two actions defined to do this, calendar() and bulk() respectively. I also choose the form fields yaml file to be loaded by setting the $formConfig public property. My controller looks something like this:
class AvailableCars extends Controller
{
public $formConfig = 'config_form.yaml';
public function bulk($recordId = null, $context = null)
{
$this->pageTitle = "Bulk update rates";
$model = $this->formFindModelObject($recordId);
$this->initForm($model);
}
public function calendar($recordId = null, $context = null)
{
$this->pageTitle = "Update rates for single date on calendar";
$model = $this->formFindModelObject($recordId);
$this->initForm($model);
}
public function onSave($recordId = null, $context = null)
{
$this->formFindModelObject($recordId)->save();
Flash::success("Rates saved successfully");
}
}
The problem is that this works for one of the actions, however if I put, for example:
$this->formConfig = 'alternate_fields.yaml';
in either of the bulk() or calendar() methods, it does not override the behavior and load a different form config yaml file; and it even errors out if it is not previously defined as a class property. So I can assume this yaml file is loaded before either of these methods are called.
So my question is, is there any way to load a dynamic formConfig yaml file based on the entry point? Or, is this even good practice in Laravel / October, or should each controller only be responsible to do one thing, and have only one way to create/read/update/destroy a model?
You can set configuration file manually as per your need. but we also need to follow some rules and add required methods with proper naming conventions for it.
and yes every thing is good practice if we do it properly :)
You can use this code for update existing records.
public function updatebulk($recordId = null, $context = null)
{
$this->pageTitle = "Bulk update rates";
$this->asExtension('FormController')->setConfig('bulk_config.yaml');
$this->asExtension('FormController')->update($recordId, $context);
}
public function updatebulk_onSave($recordId = null, $context = null) {
$this->asExtension('FormController')->setConfig('bulk_config.yaml');
$this->asExtension('FormController')->update_onSave($recordId, $context);
// or custom logic (if you add custom logic please remove above lines)
}
Now you can navigate to http://localhost/backend/author/plugin/controller_name/updatebulk/1 it will render form based on new bulk_config.yaml configuration file.
and when you save it, it will call updatebulk_onSave to update record. we must follow rule and let FormController handle all control to make it work correctly.
If you need to save record differently then you need to add custom logic in updatebulk_onSave method its up to you.
#Note
If you also need creation functionality you need additional methods. For ex.
public function createbulk($context = null)
{
$this->pageTitle = "Bulk create rates";
$this->asExtension('FormController')->setConfig('bulk_config.yaml');
$this->asExtension('FormController')->create($context);
}
public function createbulk_onSave($context = null) {
$this->asExtension('FormController')->setConfig('bulk_config.yaml');
$this->asExtension('FormController')->create_onSave($context);
}
if any doubts please comment.
So this question comes from another i created you can find here But i have changed my code so now it looks something like this:
Car Controller
public function indexAction(){
$category = new Application_Model_CarMapper();
$gcat = $this->getRequest()->getPathInfo();
//get id
$id = $category->find_id_by_name($gcat);
$this->view->title = $category->get_sub_cat_select($id);
}
This is a new function i created inside the mapper:
public function find_id_by_name($name){
$select = $this->getDbTable()->query("SELECT * FROM car_category WHERE category_name = '{$name}'");
$result = $select->fetchAll();
if(!$result) {
return;
}
return $result[0]['ID'];
}
I am testing it out by the title but it just doesnt seem to display at all. I would like it to display the drop down menus for the specific category, e.g
car-live.local/cars/Volvo ---> "Welcome to the Volvo Car Finder"
car-live.local/cars/BMW ---> "Welcome to the BMW Car Finder"
I know it is not working as i have to split down the URL even more, as right now it is finding the id via the URL, but i am unsure how to do this :s Any light you can shed on this would be extremely grateful.. Thanks.
[EDIT]
New code:
public function indexAction(){
$category = new Application_Model_CarMapper();
$gcat = explode("/", $this->getRequest()->getPathInfo());
if(isset($gcat['category_name'])) {
$id = $category->find_id_by_name($gcat);
$this->view->title = $category->get_sub_cat_select($id);
}
}
I'm not sure if I understood correctly your issue, but from code it seems like you're having issues dealing with the URL. So, let's take this example:
car-live.local/car/Volvo
After you get at indexAction you should explode by the /.
This is return an array with all the components of the URL, then you take the last one in this case would be Volvo and send it to your find_id_by_name() method.
My usual approach to this problems is to follow something like:
Explode by slashes;
Get the first one (in this case car and "route" it to a cars class, by sending to the constructor that and the rest of the request;
The car class constructor then takes the next part of the URL Volvo and "routes" it an appropriate method like getVolvo() or whatever you need...
You can route it to what you need, but just try to delegate the responsibilities of processing the URL to the appropriate classes. Example:
/cars/whatEverCar => Should be processed by class car
/bike/whatEverBike => Should be processed by class bike
Hope this helps.
Edit:
Your find_id_by_name() method can have some URL like:
/car/find_id_by_name/Volvo
Am I explaining this well? You can take parts of the URL and call methods directly... and send the rest of the URL.
Edit 2: Code example...
The following code is a very nice solution for your problem:
<?php
// Call this class as soon the site stars
class Router {
public function __construct() {
// In some really cool way get your full URL, for example I'm gonna use a string...
$fullURL = "example.com/Car/findCarById/Volvo";
array_shift($fullURL); // removes "example.com", obvious stuff...
$explodedUrl = explode("/", $fullURL);
$targetClass = $explodedUrl[0]; // Temporary store the target is "Car"
array_shift($fullURL); // Removes "Car", we don't need it anymore...
$target = new $targetClass($fullURL); // call the Car class responsible for handling all the "Car" related stuff!
}
}
class Car {
public function __construct($request) {
// $request is an array with "findCarById", "Volvo"
$targetMethod = $request[0]; // Temporary store the target is "findCarById"
array_shift($request);
$this->$targetMethod($request);
}
private function findCarById($parameter) {
// $parameter is an array with "Volvo"..
// Now to your query and other cool stuff with $parameter[0];
print "I'm the method findCarById() of the class Car called by the main router to find the ID for {$parameter}...";
}
}
?>
If you need to add more functions to your Car class, just add another method and then call it over the URL with his name, same way I did with findCarById().
Warning: This code might have minor bugs, it was written on Notepad and IS NOT tested.
i need differents results from a model but i don't understand if it is correct make a single call and leave to model all the work or make more calls and collect the result to pass to the view when tables aren't joined or when i need fetch one row from a table and differents rows from others.
First example (more calls, collect and send to view):
CONTROLLER
// call functions of model
$modelName = new Application_Model_DbTable_ModelName();
$rs1 = $modelName->getTest($var);
$rs2 = $modelName->getTest2($var2);
// collect data
$pippo = $rs1->pippo;
if ($rs2->pluto == 'test') {
$pluto = 'ok';
} else {
$pluto = 'ko';
}
// send to view
$this->view->pippo = $pippo;
$this->view->pluto = $pluto;
MODEL
public function getTest($var) {
...
select from db...
return $result;
...
}
public function getTest2($var) {
...
select from db...
return $result;
...
}
Second example (one call, model collect all data, return to controller and send to view):
CONTROLLER
// call one function of model
$modelName = new Application_Model_DbTable_ModelName();
$rs = $modelName->getTest($var);
MODEL
public function getTest($var) {
...
select from db...
if ($result > 0) {
call other function
call other function
collect data
return $result;
...
}
Thanks
There's no one correct answer to this question, but in general, you should endeavor to keep your business logic in one place. Think of it as, "thin controller, thick model." I.e., keep the controllers as small and simple as possible and put all the business logic in the models.
There seems to be a few questions here:
But if i don't need to interact with db and i need only a simply
function is better put that function in model? For example:
CONTROLLER:
public function printAction() {
$data = $this->getRequest()->getPost();
$label = "blablabla";
$this->view->label = $label;
}
first, in the context of Zend Framework this particular example doesn't make much sense. The whole point of the controller is to populate the view template. However, I do get the idea. I would point you to Action Helpers and View helpers as a means to address your concerns. You can always add a utility class to your library for those pieces of code that don't seem to fit anywhere else.
Action Helpers typically are employed to encapsulate controller code that may be repetitive or reusable. They can be as simple or as complex as required, here is a simple example:
class Controller_Action_Helper_Login extends Zend_Controller_Action_Helper_Abstract
{
/**
* #return \Application_Form_Login
*/
public function direct()
{
$form = new Application_Form_Login();
$form->setAction('/index/login');
return $form;
}
}
//add the helper path to the stack in the application.ini
resources.frontController.actionhelperpaths.Controller_Action_Helper = APPLICATION_PATH "/../library/Controller/Action/Helper"
//the helper is called in the controller
$this->_helper->login();
a View helper does the same thing for the view templates:
class Zend_View_Helper_PadId extends Zend_View_Helper_Abstract
{
/**
* add leading zeros to value
* #param type $id
* #return string
*/
public function padId($id)
{
return str_pad($id, 5, 0, STR_PAD_LEFT);
}
}
//in this example the helper path is added to the stack from the boostrap.php
protected function _initView()
{
//Initialize view
$view = new Zend_View();
//add custom view helper path
$view->addHelperPath('/../library/View/Helper');
//truncated for brevity
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer');
$viewRenderer->setView($view);
//Return it, so that it can be stored by the bootstrap
return $view;
}
//and to use the helper in the view template
//any.phtml
<?php echo $this->padId($this->id) ?>
i need differents results from a model but i don't understand if it is
correct make a single call and leave to model all the work or make
more calls and collect the result to pass to the view when tables
aren't joined or when i need fetch one row from a table and differents
rows from others.
This question is more about structure then about correctness.
You can interact with your database table models in Action and View helpers for simple/repetitive queries if you need to, however most developers might frown on this approach as being difficult to maintain or just ugly.
Many people seem to favor Doctrine or Propel to help them manage their database needs.
At this point I like to roll my own and currently favor domain models and data mappers, not an end all be all pattern, but seems to be appropriate to your question.
This is not a simple suggestion to implement for the first time, however i found two articles helpful to get started:
http://phpmaster.com/building-a-domain-model/
http://phpmaster.com/integrating-the-data-mappers/
and if you really want to get into it try:
http://survivethedeepend.com/
I hope this answers at least a part of your questions.
I want to show/hide the new link action of the admin generator list depending of some db condition.
For example:
A "Group" have many "Evaluation", a teacher may create new evaluations only if the Group status is not ended. I want to hide the "new" link of the symfony admin generator list depending on that. How can I do it?, I tried editing the _list_actions file with no success until now.
thanks.
Yoan
I think you can make it in a few ways.
You can hide link to the new action, but this is not very good,because users can create new evaluations with direct link.
So I recommend you next way.
Go to cache/backend/prod/modules/autoNamemodule/action/action.class.php
Copy to apps/backend/modules/Namemodule/action/action.class.php
next
public function executeNew(sfWebRequest $request)
{
$this->form = $this->configuration->getForm();
$this->product = $this->form->getObject();
}
Than you need to check status. I do not now yon DB table name, so for example
public function executeNew(sfWebRequest $request)
{
$id = $request->getParameter('id', false);
if (ctype_digit($id)) {
$group = Doctrine::getTable('Group')->findOneById($id);
$group_status=$group->getStatus();
if($group_status== 0){
$this->form = $this->configuration->getForm();
$this->product = $this->form->getObject();
}
else {
$this->getUser()->setFlash('notice', 'Group status ended!You can not create new evaluations ' );
$this->redirect('#yourmodulenamerout');
}
}
So if Group status ended you redirect user to the index of you backend module and show user why he can not create new evaluations). You can also hide link to the new actions. In the same way but you must make it in _list_actions file so it is not very good practice.