Phalcon use Phalcon\Tag OR Phalcon\Forms for creating forms - forms

I searched up and down but couldn't find which one is better Phalcon\Tag OR Phalcon\Forms for creating forms.
Both classes have functionality to create form elements. But I found there are some handy tags in the Phalcon\Tag, for example Phalcon\Tag::emailField() or Phalcon\Tag::dateField(array())
Phalcon documentation says:
"Phalcon\Forms is a component that aid the developer in the creation
and maintenance of forms in web applications."
"Phalcon\Tag is designed to simplify building of HTML tags. It
provides a set of helpers to generate HTML in a dynamic way."
Can anybody help me with the pros and cons of using both the methods.
Thanks

In simple meaning Phalcon\Tag are used to design only html (users view). but for validation && adding rules to the form you need to use phalcon\forms i will show you an example of phalcon\forums below
NEW FORM CLASS:
use Phalcon\Forms\Form,
Phalcon\Forms\Element\Password,
Phalcon\Forms\Element\Email as Emailfield,
Phalcon\Forms\Element\Check,
Phalcon\Forms\Element\Hidden,
Phalcon\Validation\Validator\PresenceOf,
Phalcon\Validation\Validator\Identical,
Phalcon\Validation\Validator\Email;
class LoginForm extends Form
{
public function initialize()
{
$email = new Emailfield('email', array(
'placeholder' => 'Type your Email'
));
$email->setLabel('E-Mail');
$email->setFilters('email');
$email->addValidators(array(
new PresenceOf(array(
'message' => 'E-mail is required'
)),
new Email(array(
'message' => 'E-mail is not valid'
))
));
$this->add($email);
$password = new Password('password', array(
'placeholder' => 'Type your Password'
));
$password->setLabel('Password');
$password->setFilters(array('striptags', 'string'));
$password->addValidators(array(
new PresenceOf(array(
'message' => 'Password required'
))
));
$this->add($password);
//Remember
$long_login = new Check('long_login', array(
'value' => 'yes'
));
$long_login->setLabel('Keep me logged in');
$this->add($long_login);
// CSRF
$csrf = new Hidden('csrf');
$csrf->addValidator(new Identical(array(
'value' => $this->security->getSessionToken(),
'message' => 'CSRF validation failed'
)));
// $this->add($csrf);
}
}
In Controller:
$form = new LoginForm();
if (!empty($_POST)) {
if (!$form->isValid($_POST)) {
$errors = array();
foreach ($form->getMessages() as $message) {
$errors[] = $message;
}
if (!empty($errors))
$this->flash->error(join('<br/>', $errors));
} else {
//Login Continues
}
}
$this->view->setVar('form', $form);
To convert this form to html below is the code:
<div class="form-group">
{{ form.label('email',['class': 'control-label']) }}
{{ form.render('email', ['class': 'form-control input-md']) }}
</div>
<div class="form-group">
{{ form.label('password',['class': 'control-label']) }}
{{ form.render('password', ['class': 'form-control input-md']) }}
</div>
<div class="checkbox">
{{ form.render('long_login') }}
{{ form.label('long_login') }}
</div>

Really great example in general but I'm struggling with the flash message. After a quick google search I was more confused after reading the documentation. Some say that $this->flash->output() should be placed to the view to see the flash messages. Though this causes errors and I believe it's something from the past. Can somebody tell me what flash method should I place to view to see the flash messages or Where I'm going wrong?
EDIT: Well I managed to get \Phalcon\Flash\Session to work and I believe it's event more suitable for me than Direct. For that you need to register flash service in Dependency Injector in config/services.php. I also replaces classes with bootstrap classes.
$di->set('flash', function() {
return new Phalcon\Flash\Session(array(
'error'=>'text-danger',
'warning'=>'text-warning',
'notice'=>'text-info',
'success'=>'text-success'
));
});

It depends on which you are working.
If we say, you need to insert lots of data with lots of validations and it may change during progress then its far better to use "Phalcon\Forms"
As they are very dynamic.
Means you can change text-box with select box very easily without touching template.
You can add validations without worrying about template and other stuff.
And for-most reusable you can reuse form if you need.
so if there is less data then you are free to use anyone of that but more suggestible is "Phalcon\Forms" its very dynamic and structural.

Related

Sonata Admin Bundle clickable Field

I'll describe a little bit the architecture of my models to understand my problem: I'm developing a Symfony2 web-app.
And I installed the sonataMongoDB Admin Bundle to create my Admin part.
The application is an online Quizzer in fact I have a document User which reference many documents Quizz. when I'm displaying the users list I need that the quiz field become clickable to go inside the quiz and see the results.
Here is the code of the ConfigureListFields function:
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('email')
->add('firstName')
->add('lastName')
->add('quizz', null, array('label' => 'Quiz Passd : Result'))
->add('_action', 'actions', array(
'actions' => array(
'inscription' => array('template' => 'ATSAdminBundle:CRUD:list__action_inscription.html.twig'),
'edit' => array(),
)
))
;
}
And here how I get my Quiz object:
public function __toString()
{
return $this->getResult() ;
}
But I want that the Quiz Field become clickable not displaying like a simple String.
I think by default the list view will not link one-to-many objects.
You can do so by creating a custom template (just like you did with actions) where you can loop through the quizes and link them e.g.:
{% block field %}
<div>
{% foreach object.quizzes as quizz %}
....
{% foreach %}
</div>
{% endblock %}
See https://sonata-project.org/bundles/doctrine-orm-admin/master/doc/reference/list_field_definition.html#custom-template
If a quizz is a single related object you just need another admin class for Quizz and allow the show or the edit rule. By default Sonata will link to the edit rule. So if you don't have the role for editing Quizz nothing will be linked. Maybe that is your main problem.
If that is your case try this piece of code to verfiy it:
->add('quizz', null, array('label' => 'Quiz Passd : Result', 'route' => 'show'))
Last but not least it is more common to link to show routes inside the show view of the parent object. You can then add your Quizz(es) inside the tab menu:
protected function configureTabMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null)
{
$menu->addChild($this->trans('Quizzes'), array(
'uri' => $admin->generateUrl('sonata.admin.quizz.list', array('id' => $id)),
));
}
http://sonataadminbundle.readthedocs.org/en/latest/reference/advanced.html#dropdowns-in-tab-menu
https://github.com/sonata-project/SonataAdminBundle/issues/2883

Laravel, Form::select unable to update many to many relationship

I'm new to Laravel, and I'm being dumb on this for sure, cause i've read the documentation and i've searched all over google but i'm not getting how to go over this. I have a M:M relationship between galleries and artist. Inside each gallery edit page I have a form to update the name and url for the gallery that is working fine. In the same page I have 2 other select forms, one for adding artists to the gallery and another to remove artists, that need to update a pivot table called "galleries_artists". I created 2 custom methods for both these forms called "postAdd" and "postRemove" but I can't put them to work regardless of what I try.
Below is the code i have so far. Hope somebody can help me understand the dumb mistakes i'm making.
Model - Artist.php
class Artist extends Eloquent {
protected $fillable = array('name');
public static $rules = array(
'name'=>'required|min:2'
);
public function galeries() {
return $this->belongsToMany('Gallery', 'galeries_artists', 'artist_id', 'gallery_id', 'stand_id');
}
}
Model - Gallery.php
class Gallery extends Eloquent {
protected $fillable = array('name', 'stand_id', 'url');
public static $rules = array(
'stand_id'=>'required|integer'
);
public function stand() {
return $this->belongsTo('Stand');
}
public function artist() {
return $this->belongsToMany('Artist', 'galleries_artists', 'gallery_id', 'artist_id', 'stand_id');
}
}
Controller - GalleriesController.php
public function postAdd($id, $aid) {
$input = array_except(Input::all(), '_method');
$v = Validator::make(Input::all(), Artist::$rules);
if ($v->passes()) {
$gallery = Gallery::find($id);
$add_artist = Input::get();
$add_artist->galleries()->attach(Input::get('add_artist'));
$add_artist->save();
return Redirect::route('admin.galleries.edit')
->with('message', 'Artist added successfully.');
}
return Redirect::route('admin.galleries.edit')
->with('message', 'Something went wrong')
->withErrors($v)
->withInput();
}
public function postRemove($id, $aid) {
$input = array_except(Input::all(), '_method');
$v = Validator::make(Input::all(), Artist::$rules);
if ($v->passes()) {
$gallery = Gallery::find($id);
$remove_artist = Input::get();
$remove_artist->galleries()->detach(Input::get('remove_artist'));
$remove_artist->save();
return Redirect::route('admin.galleries.edit')
->with('message', 'Artist removed successfully.');
}
return Redirect::route('admin.galleries.edit')
->with('message', 'Something went wrong')
->withErrors($v)
->withInput();
}
edit.blade.php
Add Form
{{ Form::open(array('class' => '', 'method' => 'put', 'action'=> array('GalleriesController#postAdd', $gallery->id , $add_artist->id ))) }}
<div class="form-group">
{{ Form::label('Add Artist:') }}
{{ Form::select('add_artist', $other_artists_name, null, array('class'=>'form-control')) }}
</div>
{{ Form::button('Add Artist', array('type' => 'submit', 'class'=>'btn btn-primary')) }}
{{ Form::close() }}
edit.blade.php
Remove Form
{{ Form::open(array('class' => '', 'method' => 'put', 'action'=>array('GalleriesController#postRemove', $id , 'aid'))) }}
<div class="form-group">
{{ Form::label('Remove Artist:') }}
{{ Form::select('remove_artist', $gallery_artists_name, null, array('class'=>'form-control')) }}
</div>
{{ Form::button('Remove Artist', array('type' => 'submit', 'class'=>'btn btn-danger')) }}
{{ Form::close() }}
Routes.php
Route::post('admin/galleries/{galleries}/add/{aid}', 'GalleriesController#postAdd');
Route::post('admin/galleries/{galleries}/remove/{aid}', 'GalleriesController#postRemove');
Route::resource('admin/galleries', 'GalleriesController');
I've been doing so many changes to the code that a lot of things might be mixed up. Sorry if that's the case.
You are making it pretty difficult. Here is what I did, but with checkboxes, which allowed me to cut down on the number of functions and forms I needed to work with. I've skipped the validation, but what I do have was tested and seems to work fine.
Swapping out the checkboxes for a select shouldn't be too much additional work, but I'd suggest going with a multi-select in that case, because again, it would be much simpler to work with for you and much easier to use from the user's standpoint. Let me know if I should modify my answer if it has to be selects.
Controller
class ArtController extends BaseController {
public function getIndex($artist_id)
{
// Get our artist with associated galleries
$artist = Artist::find($artist_id);
$artist->load('galleries');
// Get all galleries to populate our checkboxes
$galleries = Gallery::all();
// Show form
return View::make('art.gallery_update_form')->with('artist', $artist)->with('galleries', $galleries);
}
public function postIndex($artist_id)
{
// Grab our artist
$artist = Artist::find($artist_id);
// Sync the galleries. If no galleries were chosen, send it an empty array. Sync will perform both write and delete operations for you in one shot. Very handy.
$artist->galleries()->sync(Input::get('galleries', array()));
// Reshow the form
return $this->getIndex($artist_id);
}
}
View
#section('content')
{{ Form::open() }}
<!-- Get a list of our ID's so we can check/uncheck the checkboxes as we go -->
<?php $artist_galleries = $artist->galleries->lists('id'); ?>
<!-- Create checkbox for each gallery -->
#foreach($galleries as $gallery)
{{ Form::label($gallery->id, $gallery->name) }}
<!-- 3rd parameter is where the magic happens, it's looking in our list of galleries
we created a few lines up for the id to see if the artist belongs to that gallery or not -->
{{ Form::checkbox('galleries[]', $gallery->id, in_array($gallery->id, $artist_galleries), array('id' => $gallery->id)) }}
<br />
#endforeach
{{ Form::submit() }}
{{ Form::close() }}
#stop
Routes
Route::get('art/{artist_id}', array('uses' => 'ArtController#getIndex'));
Route::post('art/{artist_id}', array('uses' => 'ArtController#postIndex'));
Edit
Just getting your postAdd() method to work, all that should be required is something like this...
public function postAdd($id, $aid)
{
$input = array_except(Input::all(), '_method');
$v = Validator::make(Input::all(), Artist::$rules);
if ($v->passes()) {
$gallery = Gallery::find($id);
$gallery->artists()->attach($aid);
return Redirect::route('admin.galleries.edit')
->with('message', 'Artist added successfully.');
}
return Redirect::route('admin.galleries.edit')
->with('message', 'Something went wrong')
->withErrors($v)
->withInput();
}
I may be a little confused on the purpose of this function. As I wrote it, it will attach a selected artist to a gallery. I'm not sure of the purpose of using input. It looks as though you may also be attempting to save a new artist as though you wish your users to be able to create a new artist and assign that artist to a gallery when the artist is created? If you are passing in the artist id and the gallery id, that should be all you need and there is no need for the Input class.
Usually though, you'd be only passing in the gallery id which you would have generated a link for and it would be in the URI and the artist would be passed in via the form, in which case you would need to use Input::get('add_artist') and that would keep your URI much cleaner as well.
The postRemove() function would be the exact same, except you'd want to use detach() instead. This is all assuming of course all the rest of the functionality which is responsible for passing in the gallery id and artist id are working as well as the relationships themselves you've already setup in your models.

Symfony 2 This form should not contain extra fields

I created a form using formBuilder in Symfony. I add some basic styling to the form inputs using an external stylesheet and referencing the tag id. The form renders correctly and processes information correctly.
However, it outputs an unwanted unordered list with a list item containing the following text: This form should not contain extra fields.
I am having a really hard time getting rid of this notice. I was wondering if someone can help me understand why it being rendered with my form and how to remove it?
Many thanks in advance!
Controller
$form = $this->createFormBuilder($search)
->add('searchinput', 'text', array('label'=>false, 'required' =>false))
->add('search', 'submit')
->getForm();
$form->handleRequest($request);
Twig Output (form is outputted and processed correctly
This form should not contain extra fields.
Rendered HTML
<form method="post" action="">
<div id="form">
<ul>
<li>This form should not contain extra fields.</li>
</ul>
<div>
<input type="text" id="form_searchinput" name="form[searchinput]" />
</div>
<div>
<button type="submit" id="form_search" name="form[search]">Search</button>
</div>
<input type="hidden" id="form__token" name="form[_token]" value="bb342d7ef928e984713d8cf3eda9a63440f973f2" />
</div>
</form>
It seems to me that you have the problem because of the token field. If it is so, try to add options to createFormBuilder():
$this->createFormBuilder($search, array(
'csrf_protection' => true,
'csrf_field_name' => '_token',
))
->add('searchinput', 'text', array('label'=>false, 'required' =>false))
->add('search', 'submit')
->getForm();
To find out the extra field use this code in controller, where you get the request:
$data = $request->request->all();
print("REQUEST DATA<br/>");
foreach ($data as $k => $d) {
print("$k: <pre>"); print_r($d); print("</pre>");
}
$children = $form->all();
print("<br/>FORM CHILDREN<br/>");
foreach ($children as $ch) {
print($ch->getName() . "<br/>");
}
$data = array_diff_key($data, $children);
//$data contains now extra fields
print("<br/>DIFF DATA<br/>");
foreach ($data as $k => $d) {
print("$k: <pre>"); print_r($d); print("</pre>");
}
$form->bind($data);
This message is also possible if you added/changed fields in your createFormBuilder() and press refresh in your browser...
In this case it's ok after sending the form again ;-)
I got the same message while having multiple forms on the same page. Turns out, symfony defaults to the name 'form' for all of them. Instead of using createFormBuilder, you can change the name of the form to avoid conflicts using
public FormBuilderInterface createNamedBuilder(string $name, string|FormTypeInterface $type = 'form', mixed $data = null, array $options = array(), FormBuilderInterface $parent = null)
See https://stackoverflow.com/a/13366086/1025437 for an example.
I ran into this error when creating a multi-step form.
When the step 1 form is submitted, $request->request contains acme_mybundle_myform array. This created a validation error and stopped the back, forward and form fields from populating correctly. Not to mention "this-form-should-not-contain-extra-fields"
I discovered this thanks to the code by nni6.
The solution in my case was inside the controller:
if ($form->isValid())
{
if($form->has('nextStep') && $form->get('nextStep')->isClicked())
{
$session->getFlashBag()->set('notice', 'Next clicked');
$registerType->incrementStep();
$request->request->remove('acme_mybundle_myform');
return $this->forward("AcmeMyBundle:Default:register", array($request));
}
....
}
I had the same error.
It was because I had a form which, by mistake, had a NULL name.
In the HTML, the name attribute would look like this:
<form name href="..." action"..."></form>
As simple as that.
Might not be the case for everyone, but worth to check.

ZF: Form array field - how to display values in the view correctly

Let's say I have a Zend_Form form that has a few text fields, e.g:
$form = new Zend_Form();
$form->addElement('text', 'name', array(
'required' => true,
'isArray' => true,
'filters' => array( /* ... */ ),
'validators' => array( /* ... */ ),
));
$form->addElement('text', 'surname', array(
'required' => true,
'isArray' => true,
'filters' => array( /* ... */ ),
'validators' => array( /* ... */ ),
));
After rendering it I have following HTML markup (simplified):
<div id="people">
<div class="person">
<input type="text" name="name[]" />
<input type="text" name="surname[]" />
</div>
</div>
Now I want to have the ability to add as many people as I want. I create a "+" button that in Javascript appends next div.person to the container. Before I submit the form, I have for example 5 names and 5 surnames, posted to the server as arrays. Everything is fine unless somebody puts the value in the field that does not validate. Then the whole form validation fails and when I want to display the form again (with errors) I see the PHP Warning:
htmlspecialchars() expects parameter 1 to be string, array given
Which is more or less described in ticket: http://framework.zend.com/issues/browse/ZF-8112
However, I came up with a not-very-elegant solution. What I wanted to achieve:
have all fields and values rendered again in the view
have error messages only next to the fields that contained bad values
Here is my solution (view script):
<div id="people">
<?php
$names = $form->name->getValue(); // will have an array here if the form were submitted
$surnames= $form->surname->getValue();
// only if the form were submitted we need to validate fields' values
// and display errors next to them; otherwise when user enter the page
// and render the form for the first time - he would see Required validator
// errors
$needsValidation = is_array($names) || is_array($surnames);
// print empty fields when the form is displayed the first time
if(!is_array($names))$names= array('');
if(!is_array($surnames))$surnames= array('');
// display all fields!
foreach($names as $index => $name):
$surname = $surnames[$index];
// validate value if needed
if($needsValidation){
$form->name->isValid($name);
$form->surname->isValid($surname);
}
?>
<div class="person">
<?=$form->name->setValue($name); // display field with error if did not pass the validation ?>
<?=$form->surname->setValue($surname);?>
</div>
<?php endforeach; ?>
</div>
The code work, but I want to know if there is an appropriate, more comfortable way to do this? I often hit this problem when there is a need for a more dynamic - multivalue forms and have not find better solution for a long time.
Having no better idea, I have created a view helper that handles the logic presented above. It can be found here.
If the helper is available in the view, it can be used in the following way (with the form from the question):
<?=
$this->formArrayElements(
array($form->name, $form->surname),
'partials/name_surname.phtml'
);
?>
The contents of the application/views/partials/name_surname.phtml partial view are:
<div class="person">
<?= $this->name ?>
<?= $this->surname ?>
</div>
The fields are rendered according to the posted form and validation messages are shown only next to the values that failed validation.
The helper's code is far from perfect (I just rewrote the idea from the question) but is easy to use and can be considered as good starting point.

CakePHP: allowing database update with button click

I have a product search page with the form below. The search result is displayed on the same page with search bar at the top.
echo $this->Form->create('Searches', array('action'=>'products', 'type' => 'get', 'name' => 'textbox1'));
echo $form->input($varName1, array('label' => false));
echo $form->end('Locate');
I also have a little box next to the search result that allows (it doesn't work yet) the user to flag using checkboxes a product and accordingly update its database (table products and using model Product) with a button click. Note that I have a Searches controller for this search page.
<form method="link" action="/myapp/product/test_update_db>
<label><input type="checkbox" name="flag1" <?php echo $preCheckBox1; ?>>Flag 1</input></label>
<label><input type="checkbox" name="flag2" <?php echo $preCheckBox2; ?>>Flag 2</input></label>
<input type="submit" value="Update">
</form>
I'm having difficulty with this approach figuring out how to perform this check-box-and-DB-update routine. I'm getting to the link I'd like to go (/myapp/product/test_update_db), but I don't know how to take variables flag1 and flag2, along with row ID of this result ($results['Product']['id'])) to the new page.
Could someone guide me on how to perform this neatly? Is this general approach correct? If not, what route should I be taking? I'd prefer not to use javascript at this time, if possible.
EDIT: I think I can make this work if I use the URL for passing data.. but I'd still like to know how this could be done "under the hood" or in MVC. I feel like I'm hacking at the CakePHP platform.
UPDATE: So, I ended up using the URL parameters for retrieving information pieces like flag1 and flag2. I'm still looking for an alternative method.
To see where your is-checkbox-checked data is located, do the following in your controller:
// Cake 2.0+
debug($this->request->data);
// previous versions
debug($this->data);
If you want to pass data to your search controller from the current page, you can always add the data to your form:
$this->input
(
'Product.id',
array
(
'type' => 'hidden',
'value' => $yourProductId
)
);
I ended up using information embedded in the URL for getting submission data. Something like below..
In Products controller, when the form with flag1 and flag2 are submitted:
public function test_update_db() {
// Get variables from URL, if any, and save accordingly
$result = $this->Product->updateProduct($this->params['url'], 'url');
if ($result) {
$this->Session->setFlash('Successfully updated!', 'default', array('class' => 'success'));
$this->redirect($this->referer());
}
else {
$this->Session->setFlash('Update was unsuccessful!', 'default', array('class' => 'error'));
$this->redirect($this->referer());
}
}
This works for doing what I needed to do. I feel like there's a more proper way to do this though.
if ($result) {
$this->Session->setFlash('Successfully updated!', 'default', array('class' => 'success'));
$this->redirect($this->referer());
}