Search This Blog

Wednesday, February 13, 2013

Symfony project sfservermon post 4 - edit forms.

This is post 4 in the series and covers the edit forms. The server and logs listing pages are discussed in post 3  here

The existing phpservermon application has edit forms for the server list, users and configuration data.

As the forms (with the exception of the configuration data) map well to the Symfony forms framework, the forms for sfservermon were generated using the Sensio Generator Bundle

Using the Sensio Generator Bundle

The servers edit forms were generated:


$ app/console generate:doctrine:crud \
> --entity=JMPRServerMonBundle:MonitorServers

                                          
  Welcome to the Doctrine2 CRUD generator 
                                          


This command helps you generate CRUD controllers and templates.

First, you need to give the entity for which you want to generate a CRUD.
You can give an entity that does not exist yet and the wizard will help
you defining it.

You must use the shortcut notation like AcmeBlogBundle:Post.

The Entity shortcut name [JMPRServerMonBundle:MonitorServers]:

By default, the generator creates two actions: list and show.
You can also ask it to generate "write" actions: new, update, and delete.

Do you want to generate the "write" actions [no]? yes

Determine the format to use for the generated CRUD.

Configuration format (yml, xml, php, or annotation) [annotation]: yml

Determine the routes prefix (all the routes will be "mounted" under this
prefix: /prefix/, /prefix/new, ...).

Routes prefix [/monitorservers]: /servers

                            
  Summary before generation 
                            

You are going to generate a CRUD controller for "JMPRServerMonBundle:MonitorServers"
using the "yml" format.

Do you confirm generation [yes]?

                  
  CRUD generation 
                  

Generating the CRUD code: OK
Generating the Form code: OK
Confirm automatic update of the Routing [yes]?
Importing the CRUD routes: FAILED

                                                                  
  The command was not able to configure everything automatically. 
  You must do the following changes manually.                     
                                                                   

- Import the bundle's routing resource in the bundle routing file
  (/Users/johnreidy/sfprojects/sfservermon/webserver/src/JMPR/ServerMonBundle/Resources/config/routing.yml).

    JMPRServerMonBundle_servers:
        resource: "@JMPRServerMonBundle/Resources/config/routing/monitorservers.yml"
        prefix:   /servers

This does the following:
  • Creates a routing file in <bundle>/Resources/config/routing/monitorservers.yml
  • Creates a controller class MonitorServersController in <bundle>/Controllers/MonitorServersController.php
  • Creates a form type class MonitorServersType in <bundle>/Form/MonitorServersType.php
  • Created index, edit, new and show .twig.html view in <bundle>//Resources/views/MonitorServers
Once the servers.html.twig template is edited to add the routes, the form can be navigated to:

{% block pageContent %}
<h2>servers</h2>
<div class="message"><a href="{{ path('servers_new')}}">Add new?</a></div><br/>
<table cellpadding="0" cellspacing="0">
...

 <td>

   <a href="{{ path('servers_edit', { 'id': server.id })}}"><img src="/img/edit.png" alt="edit" title="Edit Server" /></a>

   &nbsp;

   <a href="javascript:sm_delete('{{ path('servers_edit', { 'id': server.id })}}', 'servers');"><img src="/img/delete.png" alt="delete" title="Delete Server" /></a>
 </td>



The generated MonitorServers/edit.html.twig template is improved:
  • Add twig reference to base.html.twig
  • Add block references.
  • Add a stylesheet editrecords.css.
  • Add reference to editrecords styles .
{# src/JMPR/ServerMonBundle/Resources/views/Default/index.html.twig #}
{% extends '::base.html.twig' %}
{% block title %}{{title}}{% endblock %}
{% block stylesheets %}
<link type="text/css" href="/css/editrecords.css" rel="stylesheet" />
{% endblock %}
{% block pageContent %}
<h1>MonitorServers edit</h1>

<form action="{{ path('servers_update', { 'id': entity.id }) }}" method="post" {{ form_enctype(edit_form) }}>
    <input type="hidden" name="_method" value="PUT" />
    {{ form_widget(edit_form) }}
    <p>
        <button type="submit">Edit</button>
    </p>
</form>

<ul class="record_actions">
    <li>
        <a class="record_actions" href="{{ path('servers') }}">
            Back to the list
        </a>
    </li>
    <li>
        <form action="{{ path('servers_delete', { 'id': entity.id }) }}" method="post">
            <input type="hidden" name="_method" value="DELETE" />
            {{ form_widget(delete_form) }}
            <button type="submit">Delete</button>
        </form>
    </li>
</ul>

    <input type="hidden" name="_method" value="PUT" />
    {{ form_widget(edit_form) }}
    <p>
        <button type="submit">Edit</button>
    </p>

{% endblock %}

The server edit form can be viewed:




This works, but has issues. We need to:
  • Change the port field,
  • Change to type field to a select menu.
  • Remove the un-needed fields.
Also we should add validation using the Symfony validation scheme.

New Record Defaults:

For compatibility with PhpServermon, the following fields should be set to these defaults in the newAction in the MonitorServersController class.


    public function newAction()
    {
        $entity = new MonitorServers();
        $entity->setStatus('on');
        $entity->setType('service');
        $entity->setActive('yes')
        $entity->setEmail('yes')
        $entity->setSms('no');
        $form   = $this->createForm(new MonitorServersType(), $entity);


Also in the updateAction method, this will set the Error to a non null value before it is saved.
    public function createAction(Request $request)
    {
        $entity  = new MonitorServers();
        $entity->setError('');
        $form = $this->createForm(new MonitorServersType(), $entity);


Initial Edit.

This is done by changing add calls from the BuildForm method in MonitorServersType. See the forms reference.
    public function buildForm(FormBuilderInterface $builder, array $options)

    {

        $builder

            ->add('label')

            ->add('ip','url', array('label'=>'Domain/IP', 'attr'=>array('size'=>60)))

            ->add('port')

            ->add('type', 'choice', array('choices'=>array('service'=>'Service', 'host'=>'Host', 'website'=>'Website')))
            ->add('active', 'choice', array('choices'=>array('yes'=>'Yes', 'no'=>'No')))
            ->add('email', 'choice', array('choices'=>array('yes'=>'Yes', 'no'=>'No')))
            ->add('sms', 'choice', array('choices'=>array('yes'=>'Yes', 'no'=>'No')))
        ;
    }

In particular:
  • Un-needed fields are removed,
  • Uhe order is changed,
  • Ip/address field has size set to 60.
  • A label is added for the ip field,
  • Choice fields and choices arrays are set for the drop down menus.
Now the form looks like this:

This is better, but the rendering of the form gives us limited options.
An alternative is to render each form in the template - for exmaple in table, but I was keen to use the builder method in the Type class and the  form_widget(edit_form) statement

So if we want to use the simple form_widget statement, we need to modify the css used.
To set the sytle class, add a class item to the attr array and add a label_attr item - with a class item.
i.e:
        $builder
            ->add('label', null, array('attr' => array('class' =>'edit_field'), 'label_attr' => array('class' => 'edit_label')))


This can be done by setting style classes for the form labels and the field, in editrecords.css:
.edit_label
{
width: 120px;
float: left;
}

.edit_field
{
width: 240px;
float: right;
left:200px;
}
Disclaimer - IAMAHD (I Am Not A Html Designer), it is quite likely that there is better css that this.
The builder call becomes:
        $builder
            ->add('label',null, array('attr' => array('class' =>'edit_field'), 'label_attr' => array('class' => 'edit_label')))
            ->add('ip','url',array('label'=>'Domain/IP', 'attr'=>array('size'=>60, 'class' => 'edit_field'), 'label_attr' => array('class' => 'edit_label')))
            ->add('port',null, array('attr' => array('class' =>'edit_field'), 'label_attr' => array('class' => 'edit_label')))
            ->add('status',null, array('attr' => array('class' =>'edit_field'), 'label_attr' => array('class' => 'edit_label')))
            ->add('type','choice', array('choices'=>array('service'=>'Service', 'host'=>'Host', 'website'=>'Website'), 'attr' => array('class' =>'edit_field'), 'label_attr' => array('class' => 'edit_label')))
            ->add('active', 'choice', array('choices'=>array('yes'=>'Yes', 'no'=>'No'),'attr' => array('class' =>'edit_field'), 'label_attr' => array('class' => 'edit_label')))
            ->add('email','choice', array('choices'=>array('yes'=>'Yes', 'no'=>'No'),'attr' => array('class' =>'edit_field'), 'label_attr' => array('class' => 'edit_label')))
            ->add('sms','choice', array('choices'=>array('yes'=>'Yes', 'no'=>'No'),'attr' => array('class' =>'edit_field'), 'label_attr' => array('class' => 'edit_label')))
        ;

And the form:



The layout can be improved but it ok for an initial release.

Handling Deletes.

Servermon uses http get rather than post to request a delete, so the requirement was removed from the route:

servers_delete:
    pattern:  /{id}/delete
    defaults: { _controller: "JMPRServerMonBundle:MonitorServers:delete" }
    requirements: {  }
and the controller changed:
    public function deleteAction(Request $request, $id)
    {
        $em = $this->getDoctrine()->getManager();
        $entity = $em->getRepository('JMPRServerMonBundle:MonitorServers')->find($id);

        if (!$entity) {
            throw $this->createNotFoundException('Unable to find MonitorServers entity.');
        }

        $em->remove($entity);
        $em->flush();

        return $this->redirect($this->generateUrl('server_mon_homepage'));
    }





No comments:

Post a Comment