Symfony Form: Set default/value on error? - php

My crazy designer would like the message "Required" displaying (in red) inside a field if the form has been submitted and it is invalid because of an empty field.
The form in question is a login prompt and I'm using a custom class that extends sfGuardFormSignin
I've managed to set the value and add a class with..
$this->widgetSchema['username']->setAttribute('class','red');
$this->widgetSchema['username']->setDefault('Required');
..but how do I do this only when the username field is invalid and because of the Required error?
I assume it's the same for the password field?
Many thanks in advance
EDIT:
Thanks for the advice greg0ire. I've had a play with that but the formatRow method of sfWidgetFormSchemaFormatter doesn't seem to be getting hit. Is this because my form extends sfGuardFormSignin and using the sfGuardAuth plugin?
class FrontendsfGuardFormSignin extends sfGuardFormSignin
{
public function configure()
{
parent::configure();
// This works!
$this->widgetSchema['username']->setLabel('Email');
// I copied this from the link you pasted
$decorator = new myWidgetFormSchemaFormatterCustom($this->getWidgetSchema());
$this->widgetSchema->addFormFormatter('custom', $decorator);
$this->widgetSchema->setFormFormatterName('custom');
}
}
/lib/widget/myWidgetFormSchemaFormatterCustom.class.php
class myWidgetFormSchemaFormatterCustom extends sfWidgetFormSchemaFormatter
{
public function __construct(sfWidgetFormSchema $widgetSchema)
{
parent::__construct($widgetSchema);
}
public function formatRow($label, $field, $errors = array(), $help = '', $hiddenFields = null)
{
// Nothing happens!?
var_dump($errors);
die();
parent::formatRow($label, $field, $errors, $help, $hiddenFields);
}
}

$widget->render(array('value' => $widget->getError()));

Designers have such crazy ideas...
You'll have to write a custom schema formatter to do this. You'll probably have to override the formatRow() method to achieve this.
Analyse the $errors array argument of this method, and if you spot the "Required" error in it, then do your special stuff. You won't need to use the code you posted in your question.

Related

Bolt CMS Events

I am working with the save event but having limited luck.
I have currently tried two ways but to limited success.
1) I can either never get the function to fire,
2) I am not too sure what to pass into the function for method two.
All I am trying to do is to dump the event information out on content save.Any help greatly appreciated, really loving this CMS
Attempt One -- never runs the function at all
class Extension extends BaseExtension
{
public function initialize() {
$this->addCss('assets/extension.css');
$this->addJavascript('assets/start.js', true);
$this->app['dispatcher']->addListener(\Bolt\Events\StorageEvents::POST_SAVE, 'postSave');
}
function postSave(\Bolt\StorageEvent $event)
{
dump($event);
}
Attempt two -- what do I input as a parameter?
class Extension extends BaseExtension
{
public function initialize() {
$this->addCss('assets/extension.css');
$this->addJavascript('assets/start.js', true);
$this->app['dispatcher']->addListener(\Bolt\Events\StorageEvents::POST_SAVE,$this->postsave($this->?????));
}
function postSave(\Bolt\StorageEvent $event)
{
dump($event);
}
Hopefully my answer doesn't come too late!
You simply can modify the content and save it back to the database:
public function postSave(\Bolt\Events\StorageEvent $event) {
// get the content
$content = $event->getContent();
// get a field out of the contenttype
$data = $content->get("myField");
// now modify $data here
$data = "new data - what ever you want";
// set data to the content
$content->setValue("data", $data);
// write the modified content to the database
$this->app['storage']->saveContent($content);
}
Note that the function gets fired every time you save contents. So just add an if-statement like this to just modify content you really want to:
if ($event->getContentType() == "my_type")
The parameter needed is a php callback the format for this is something like this:
$this->app['dispatcher']->addListener(\Bolt\Events\StorageEvents::POST_SAVE, array($this, 'postSave'));
That syntax is saying to run the postSave method within the current class. So this would work with your example number 1.
Now you can dump the event in your postSave method and see the results.

I just can't extend the Symfony2 Form class. Tried everything

I have literally tried everything to try and extend the Symfony2 Form class.
I want to add a new method to Form and call it in a controller:
$form = $this->createForm($this->get('my_form_service'), $entity);
$form->myNewMethod();
Because the Form class isn't defined as a service and is instantiated in another class (FormBuilder, line 221) then I can't see what I can do. I don't want to hack the core.
I could extend the controller class, e.g. the createForm() method returns the instantiated Form object:
// Extend Controller and do something with Form before returning
public function createForm($type, $data = null, array $options = array())
{
return $this->container->get('form.factory')->create($type, $data, $options);
In fact How to add a new method to a php object on the fly? shows how I could do the above and add my new method that way, but my understanding is that you need to at least add __call to the Form class - which I can't do - or the added method won't be able to use $this (which I need).
I only need to add this for when the form is used in a controller. I don't need it for any CLI processing.
Any ideas how I can add a method to the Form class without hacking the core?
====================================================================
EDIT: Why am I doing this?
I want to 'silently fail' a form submit if a certain criteria is met. I believe the best way to do this would be to do this in a controller:
if ($form->isValid()) {
if ($form->requiresSilentFail()) {
// Silently fail
} else {
// As normal, add to DB etc.
}
}
I suppose I could just do something like this:
if ($form->isValid()) {
if ($this->get('check_my_form')->requiresSilentFail($form)) {
// Silently fail
} else {
// As normal, add to DB etc.
}
}
.... but I need to also perform a little bit of extra logic in Form::handleRequest() first, so I believe extending Form is the only option.
I don't really understand why you need to extend the form class simply on your controller/service where you process the form call your method under is valid check, see below:
$form->handleRequest($request);
if ($form->isValid()) {
$this->get($apiResource->getResourceHandlerName())->update($resourceData);
$response = new Response();
$response->setStatusCode($statusCode);
if ($this->isSuccessFullStatusCode($statusCode)) {
$serializedObject = $this->getSerializer()->serialize($resourceData, 'json',$serializationContext);
$response->setStatusCode($responseStatusCode);
$response->setContent($serializedObject);
return $response;
}
}

Zend 2.0: 500 error when creating form element

I am learning Zend Framework (2.0), and I'm stuck at creating forms.
Here is the code I used (Inside a controller):
use Zend\Form\Element;
use Zend\Form\Form;
...
public function indexAction()
{
$element = new Element\Text('name');
//Nothing else
}
It always gives a 505 error, but if I comment out the line "$element ..." then it works (so the problem must be there).
Can someone point out what I have overlooked?
Also, as I see it, there are too many ways to create a form. For example, I have tried:
private function getSignupForm() {
//Create Form
$form = new Zend_Form();
$form->setAction('success');
$form->setMethod('post');
$form->setAttrib('sitename', 'mysite');
//Add Elements
//Create Username Field.
$form->addElement('text', 'username');
$usernameElement = $form->getElement('username');
$usernameElement->setLabel('Username:');
$usernameElement->setOrder(1)->setRequired(true);
return $form;
}
This way, it worked, but that is not the way the tutorial says link. So in which way should I should write it?
Thanks.

Why I can't load models in modules?! [Using HMVC in codeigniter 2.1.2]

I hope someone can help me with this one before I jump off the window. I spent few hours on this one and don't know what am I doing wrong.
Basically, I've installed HMVC in CodeIgniter 2.1.2 and everything works fine, BUT for some reason I can't load models the same way I'm doing it in standard controllers. In the old codeigniter 1.7.1 I could use it simply by invoking $this->load->model('my_model') but now I can't?!
Every single time I'm trying to load model I get this error:
A PHP Error was encountered
Severity: Notice
Message: Undefined property: Special_cart::$db
Filename: core/Model.php
Line Number: 51
I have had installed it step-by-step according to the instructions. I got third_party next to modules folder. In modules I have few modules stored like this:
modules
--boxes
----controller
----models
----views
I invoke module in my code like this:
<?=modules::run('boxes/special_cart/index');?>
My module controller code looks like this:
class Special_cart extends CI_Controller
{
public function __construct()
{
parent::__construct();
}
public function index()
{
if ($this->session->userdata('cart'))
{
# get product id's and each quantity
$cart_product_list = array_count_values($this->session->userdata('cart'));
# get list of product_id
$product_list = array_keys($cart_product_list);
# get product details
$this->load->model('productmodel');
$this->load->model('stockmodel');
$cart_products = $this->productmodel->cart_get_products_details($product_list);
$final_cart_array = array();
foreach($cart_products as $cart_product){
$product_stock = $this->stockmodel->view_product_stock($cart_product["id"]);
if(empty($product_stock) || $product_stock["UNITS"]<=0)
$cart_product["UNITS"] = 0;
else{
if($cart_product_list[$cart_product["id_web"]]>$product_stock["UNITS"])
$cart_product["UNITS"] = $product_stock["UNITS"];
else{
$cart_product["UNITS"] = $cart_product_list[$cart_product["id_web"]];
}
}
$final_cart_array[] = $cart_product;
}
$refresh_cart_array = array();
foreach($final_cart_array as $cart_product){
for($i=1;$i<=$cart_product["UNITS"];$i++){
$refresh_cart_array[] = $cart_product["id_web"];
}
}
$this->load->view("special_cart",array(
'refresh_cart_array' => $refresh_cart_array,
'final_cart_array' => $final_cart_array
));
} else {
$this->load->view("special_cart",array(
'refresh_cart_array' => NULL,
'final_cart_array' => NULL
));
}
}
}
I've tried every possible solution found on internet - none of them work....
I hope you understand my problem but in case you need some further explanation please ask me. Can anyone help?
Looks like the model you're trying to load wants to connect to the db, but the database driver is not available. If you use database queries in your application, why don't you load the database driver automatically?
Just insert "database" in the "libraries" array in application/config/autoload.php file. Don't forget to insert your database credentials into application/config/database.php.
$autoload['libraries'] = array('database');
If you need database connection just in one single model, load it before trying to access database library.
$this->load->database();
Try loading the model stating the module name as follows
$this->load->model('module_name/productmodel');
Class Models extends MX_Loader{
function getUser($username){
$sql="SELECT * FROM user WHERE username = ? ";
return $this->db->query($sql,array($username))->row();
}
}
you must using extends MX_Loader because i don't know if using CI_Model the database core cant be load in Codeigniter,,,
Try to use extend MX_Controller class (not CI_Contoller like you are doing atm)
Based on what you have wrote in comment above, I figured that you tried to create new instance of DB in module (based on chrises comment).
Do it on constuctor of Special_cart
So update current construct to be like
public function __construct()
{
parent::__construct();
$this->load->database('default');
}
(I'm writing this from top of my head, so check the methods)
Now for sure db driver should be available in your models.
Regarding issue with HMVC I dont think there are any. I'm using HMVC for a while now, and I found no problems in it (working with databases)
I had the same problem and mistake. I missed to extend controllers to MX_Controller. So the solution would be to change CI_Controller to MX_Controller like this:
class Special_cart extends MX_Controller
{
public function __construct()
{
parent::__construct();
$this->load->model('productmodel');
$this->load->model('stockmodel');
}
public function index()
{
if ($this->session->userdata('cart'))
{
# get product id's and each quantity
$cart_product_list = array_count_values($this->session->userdata('cart'));
# get list of product_id
$product_list = array_keys($cart_product_list);
# get product details
$cart_products = $this->productmodel->cart_get_products_details($product_list);
$final_cart_array = array();
foreach($cart_products as $cart_product){
$product_stock = $this->stockmodel->view_product_stock($cart_product["id"]);
if(empty($product_stock) || $product_stock["UNITS"]<=0)
$cart_product["UNITS"] = 0;
else{
if($cart_product_list[$cart_product["id_web"]]>$product_stock["UNITS"])
$cart_product["UNITS"] = $product_stock["UNITS"];
else{
$cart_product["UNITS"] = $cart_product_list[$cart_product["id_web"]];
}
}
$final_cart_array[] = $cart_product;
}
$refresh_cart_array = array();
foreach($final_cart_array as $cart_product){
for($i=1;$i<=$cart_product["UNITS"];$i++){
$refresh_cart_array[] = $cart_product["id_web"];
}
}
$this->load->view("special_cart",array(
'refresh_cart_array' => $refresh_cart_array,
'final_cart_array' => $final_cart_array
));
} else {
$this->load->view("special_cart",array(
'refresh_cart_array' => NULL,
'final_cart_array' => NULL
));
}
}
}
this is also explained in the documentation
https://bitbucket.org/wiredesignz/codeigniter-modular-extensions-hmvc/
, here the quote:
Notes:
To use HMVC functionality, such as Modules::run(), controllers must
extend the MX_Controller class. To use Modular Separation only,
without HMVC, controllers will extend the CodeIgniter Controller
class. You must use PHP5 style constructors in your controllers. ie:
<?php
class Xyz extends MX_Controller
{
function __construct()
{
parent::__construct();
}
}

Problems with redeclaring classes when using PHP's class_alias in a functional test

I'm using PHP 5.3's class_alias to help process my Symfony 1.4 (Doctrine) forms. I use a single action to process multiple form pages but using a switch statement to choose a Form Class to use.
public function executeEdit(sfWebRequest $request) {
switch($request->getParameter('page')) {
case 'page-1':
class_alias('MyFormPage1Form', 'FormAlias');
break;
...
}
$this->form = new FormAlias($obj);
}
This works brilliantly when browsing the website, but fails my functional tests, because when a page is loaded more than once, like so:
$browser->info('1 - Edit Form Page 1')->
get('/myforms/edit')->
with('response')->begin()->
isStatusCode(200)->
end()->
get('/myforms/edit')->
with('response')->begin()->
isStatusCode(200)->
end();
I get a 500 response to the second request, with the following error:
last request threw an uncaught exception RuntimeException: PHP sent a warning error at /.../apps/frontend/modules/.../actions/actions.class.php line 225 (Cannot redeclare class FormAlias)
This makes it very hard to test form submissions (which typically post back to themselves).
Presumably this is because Symfony's tester hasn't cleared the throughput in the same way.
Is there a way to 'unalias' or otherwise allow this sort of redeclaration?
As an alternate solution you can assign the name of the class to instantiate to a variable and new that:
public function executeEdit(sfWebRequest $request) {
$formType;
switch($request->getParameter('page')) {
case 'page-1':
$formType = 'MyFormPage1Form';
break;
...
}
$this->form = new $formType();
}
This doesn't use class_alias but keeps the instantiation in a single spot.
I do not know for sure if it is possible, but judging from the Manual, I'd say no. Once the class is aliased, there is no way to reset it or redeclare it with a different name. But then again, why do use the alias at all?
From your code I assume you are doing the aliasing in each additional case block. But if so, you can just as well simply instantiate the form in those blocks, e.g.
public function executeEdit(sfWebRequest $request) {
switch($request->getParameter('page')) {
case 'page-1':
$form = new MyFormPage1Form($obj);
break;
...
}
$this->form = $form;
}
You are hardcoding the class names into the switch/case block anyway when using class_alias. There is no advantage in using it. If you wanted to do it dynamically, you could create an array mapping from 'page' to 'className' and then simply lookup the appropriate class.
public function executeEdit(sfWebRequest $request) {
$mapping = array(
'page-1' => 'MyFormPage1Form',
// more mappings
);
$form = NULL;
$id = $request->getParameter('page');
if(array_key_exists($id, $mapping)) {
$className = $mapping[$id];
$form = new $className($obj);
}
$this->form = $form;
}
This way, you could also put the entire mapping in a config file. Or you could create FormFactory.
public function executeEdit(sfWebRequest $request) {
$this->form = FormFactory::create($request->getParameter('page'), $obj);
}
If you are using the Symfony Components DI Container, you could also get rid of the hard coded factory dependency and just use the service container to get the form. That would be the cleanest approach IMO. Basically, using class_alias just feels inappropriate here to me.
function class_alias_once($class, $alias) {
if (!class_exists($alias)) {
class_alias($class, $alias);
}
}
This doesn't solve the problem itself, but by using this function it is ensured that you don't get the error. Maybe this will suffice for your purpose.

Categories