====== Step3. Adding forms to allow news creating and editing. Data validation. News removal ====== ===== Adding news ===== ==== news/create.html template ==== Let's add new template create.html into crud/template/news/ folder with the following contents: Limb3 tutorial

Create news

:
:
:
:
It looks like a regular html-page but there are some small gotchas you should be aware of: * **
** tag has **runat='server'** attribute, that tells template engine that it's not a regular html-tag but a tag with so called //runtime component// (or active component) attached. Runtime component is an object that is created while template is executed and that has some specific behavior. Tags with runtime components usually have some unique identifier specified by **id** attribute. * Please note that form elements have identifiers matching the names of the fields of table "news" ==== Changes in NewsController class ==== Now we will expand our controller NewsController so that it can receive data from the form "news_form" and add news entries into the db table: request->hasPost()) return; $news = new News(); $news->import($this->request); $news->save(); $this->redirect(); } } We added doCreate() method that will be called when our application receives requests like http://%tutorial_address%/news/create. As you may have already guessed template crud/template/news/create.html will be picked and rendered automatically(similar to default "display" action). Let's examine the body of doCreate() method line by line: if(!$this->request->hasPost()) return; lmbController has request and response objects as its attributes for convenience. In the lines above we check if our application received POST request. If there is no POST request it means that the page is requested for the first time and our application should just show the create form. $news = new News(); $news->import($this->request); Here we initialize an empty News object and fill it with data from the request. News automatically imports only those fields from the request that present in the table "news" (slightly simplified description, but it's ok for now). That's why we have form fields with identifiers matching fields of the "news" table. $news->save(); Here we save new record in database and ... $this->redirect(); make redirection to the starting page of the current controller(that is /news). Let's type http://%tutorial_address%/news/create in browser and try to add a couple of news. You should see something like this: {{{:limb3:ru:tutorials:basic:simple_create.png|:limb3:ru:tutorials:basic:simple_create.png}}} ===== Adding data validation ===== Our first implementation of adding news doesn't check any fields. But in the real world applications there are usually some validation rules that have to be applied to the raw data. Let's try to implement data validation with Limb3. Ok, suppose we'd like to apply the following rules for our news: * title, date and annotation are required fields * title must be less than 75 characters long * content field is optional If user provides error input while filling create form then after submitting the form she should see the same form with all data entered into form fields as well as validation errors. To do so we will need to make some changes in News and NewsController classes and expand news/create.html template. ==== Extending News class ==== News version of News class will looks like this: addRequiredRule('title'); $validator->addRequiredRule('annotation'); $validator->addRequiredRule('date'); lmb_require('limb/validation/src/rule/lmbSizeRangeRule.class.php'); $validator->addRule(new lmbSizeRangeRule('title', 75)); return $validator; } } ?> lmbActiveRecord class performs self-validation using a validator object while saving data. Validator is created with _createValidator() method. By default _createValidator() returns an empty validator but in real applications we usually override this method. Validator - is an object of **lmbValidator** class (can be found in [[limb3:en:packages:validation|VALIDATION package]]) or any lmbValidator child class. Validator consists of a set of validation rules responsible for actual data sanity checks. Please note that the line $validator->addRequiredRule('title'); can be rewritten as lmb_require('limb/validation/src/rule/lmbRequiredRule.class.php'); $validator->addRule(new lmbRequiredRule('title')); As a result we used two validation rules here: * **lmbRequiredRule** - forces field to be present in validated data container (such as lmbActiveRecord object). * **lmbSizeRangeRule** - checks whether field has minimum and (or) maximum length ==== Changes in NewsController ==== NewsController :: doCreate() method requires some modifications too: request->hasPost()) return; $news = new News(); $news->import($this->request); $this->useForm('news_form'); $this->setFormDatasource($news); if($news->trySave($this->error_list)) $this->redirect(); } } ?> The new lines are: $this->useForm('news_form'); $this->setFormDatasource($news); if($news->trySave($this->error_list)) $this->redirect(); With the first two line we wire "news_form" runtime component of template with $news object. This allows us not to lose data in the form fields after form submission if validation fails. lmbActiveRecord :: **trySave($error_list)** method returns true if data is valid or false in case of any validation errors. trySave() is just a wrapper for save() method. lmbActiveRecord :: save() in case of any validation errors throws an exception of lmbValidationException class which is caught by trySave(). ==== Displaying validation errors by news/create.html template ==== Now let's add some code to news/create.html template to display validation errors: [...]

Create news

  {$message}
:
[...]
**** tag passes list of validation errors to tag marked with **target** attribute. Validation error messages will be rendered by and tags which we described before. Validation errors are passed to the template automatically since we wired object of News class with form runtime component in NewsController :: doCreate() method. Now try entering incorrect data and test our validation sub-system: {{{:limb3:ru:tutorials:basic:simple_create_errors.png|:limb3:ru:tutorials:basic:simple_create_errors.png}}} If you see something similar to above, then you are doing right. ===== Editing news ===== ==== news/edit.html template ==== Template for editing news crud/template/news/edit.html looks almost the same as the one for creating news: Limb3 tutorial

Edit news

  {$message}
:
:
:
:
There is a large duplication between news/create.html and news/edit.html which can be easily removed. We will optimize these templates later. ==== NewsController :: doEdit() method ==== Let's add doEdit() method to the NewsController so it can handle "edit" action: request->get('id')); $this->useForm('news_form'); $this->setFormDatasource($news); if(!$this->request->hasPost()) return; $news->import($this->request); if($news->trySave($this->error_list)) $this->redirect(); } ?> With the following line: $news = new News((int)$this->request->get('id')); a particular News object is loaded from the database using identifier retrieved from the request. Note that we tied form runtime component with $news object //before// $this->request->hasPost() check since we need to pass initial data to the template in any case. Thus form fields initially will be filled with data from $news object. ==== Modifying news/display.html ==== It's time to change news/display.html in order to display links to edit action page for each news. Let's make it an extra column in newsline table: Newsline

Newsline.

Create news

ID Date Title Actions
{$id} {$date} {$title} Edit   Delete
{$annotation}
Here we used tag, which is essentially similar to tag, but the former makes up the href attribute based on the parameters from **params** attribute. In fact, does request dispatching in reverse order i.e. it forms something like /news/edit/5 from params ('action' => 'edit', 'id' => 5). All stuff about request dispatching and so called "routes" is beyond the scope of this tutorial, but if you are interested what rules are used to parse request string you may have a look at limb/web_app/settings/routes.conf.php file (or crud/settings/routes.conf.php depending on how you started this tutorial). Let's explain how tag works: Create news This line will output something like Create news. News controller will be used by default since we are on the news controller page. The same happens for edit and delete links actions of news controller. Parameters in "params" attribute of tag are separated with commas. Accordingly Edit will generate Edit If you did everything right, you would be able to create and edit news (delete action is not implemented yet). ===== News removal ===== To remove objects from database lmbActiveRecord :: destroy() method should be used. Let's add NewsController :: doDelete() method to perform news removal: request->get('id')); $news->destroy(); $this->redirect(); } } ?> Obviously there is no need to comment this code. ===== What's next? ===== [[Step4|Step4. Templates optimization. Adding news pagination]]