Viewed   76 times

I am working on a PHP project which makes extensive use of the MVC design pattern. I am looking to add validation to a form and am curious as to what the right place for validation is.

Due to the way that forms are generated, validation on postback data is a lot simpler and less repetitive in view components. Is it acceptable to have the view validating response data, or should this be implemented within the controller, or even the model?

What are the benefits?

 Answers

3

If you're validating the data on client side (i.e Javascript validation) which is absolutely not enough and not secure at all, You should implement it in View.

If you're validating data on server side, And your validation does not require application business logic (i.e you're not checking to see if the user has enough credit in his account), You should validate in the controller.

If the validation requires business logic, Implement it inside the model and call it via controller.

Postback validation is not good since it puts lots of pressure and delay, And the only advantage is to the programmer (not to be accounted).

You can use regex for most of validation, Which has the same syntax (almost) on PHP and JS.

Tuesday, December 20, 2022
2

The controller picks up the view variable in the url and using these determines which view needs to be used. It then sets the view to be used. The view then calls the model to fetch the data it requires and then passes this to the tmpl to be displayed.

Below is a simple setup of how this all works together:

components/com_test/controller.php

class TestController extends JController
{

  // default view
  function display() {
    // gets the variable some_var if it was posted or passed view GET.
    $var = JRequest::getVar( 'some_var' );
    // sets the view to someview.html.php
    $view = & $this->getView( 'someview', 'html' );
    // sets the template to someview.php
    $viewLayout  = JRequest::getVar( 'tmpl', 'someviewtmpl' );
    // assigns the right model (someview.php) to the view
    if ($model = & $this->getModel( 'someview' )) $view->setModel( $model, true );
    // tell the view which tmpl to use 
    $view->setLayout( $viewLayout );
    // go off to the view and call the displaySomeView() method, also pass in $var variable
    $view->displaySomeView( $var );
  }

}

components/com_test/views/someview/view.html.php

class EatViewSomeView extends JView
{

  function displaySomeView($var)  {
    // fetch the model assigned to this view by the controller
    $model = $this->getModel();
    // use the model to get the data we want to use on the frontend tmpl
    $data = $model->getSomeInfo($var);
    // assign model results to view tmpl
    $this->assignRef( 'data', $data );
    // call the parent class constructor in order to display the tmpl
    parent::display();
  }

}

components/com_test/models/someview.php

class EatModelSomeView extends JModel 
{

  // fetch the info from the database
  function getSomeInfo($var) {
    // get the database object
    $db = $this->getDBO();
    // run this query
    $db->setQuery("
      SELECT 
        *
      FROM #__some_table
      WHERE column=$var
    ");
    // return the results as an array of objects which represent each row in the results set from mysql select
    return $db->loadObjectList(); 
  }

}

components/com_test/views/someview/tmpl/someviewtmpl.php

// loop through the results passed to us in the tmpl
foreach($this->data as $data) {
  // each step here is a row and we can access the data in this row for each column by 
  // using $data->[col_name] where [col_name] is the name of the column you have in your db
  echo $data->column_name;
}
Tuesday, September 20, 2022
5

The service layer can be interpreted a lot of ways, but it's usually where you have your core business processing logic, and sits below your MVC architecture, but above your data access architecture.

For example, you layer of a complete system may look like this:

  1. View Layer: Your MVC framework & code of choice
  2. Service Layer: Your Controller will call this layer's objects to get or update Models, or other requests.
  3. Data Access Objects: These are abstractions that your service layer will call to get/update the data it needs. This layer will generally either call a Database or some other system (eg: LDAP server, web service, or NoSql-type DB)

The service layer would then be responsible for:

  • Retrieving and creating your 'Model' from various data sources (or data access objects).
  • Updating values across various repositories/resources.
  • Performing application-specific logic and manipulations, etc.

The Model you use in your MVC may or may not come from your services. You may want to take the results your service gives you and manipulate them into a model that's more specific to your medium (eg: a web page).

Friday, November 25, 2022
 
3

To answer your 3th question first: No there is no easier way then what you are doing. Two lines of code to get it working can hardly be easier. Although there is a plug-in you could use, like explained in the question unobtrusive validation not working with dynamic content

Your first question, how to centralize validation, I normally use a separate class file to store all my validation rules. This way I don't have to browse through every single class file to find the rules, but have them all in one place. If that's better, is matter of choice. The main reason I started to use it, is to be able to add validation to auto-generated classes, like classes from the Entity Framework.

So I have a file called ModelValidation.cs in my data layer, and have code for all my models like

/// <summary>
/// Validation rules for the <see cref="Test"/> object
/// </summary>
/// <remarks>
/// 2015-01-26: Created
/// </remarks>
[MetadataType(typeof(TestValidation))]
public partial class Test { }
public class TestValidation
{
    /// <summary>Name is required</summary>
    [Required]
    [StringLength(100)]
    public string Name { get; set; }

    /// <summary>Text is multiline</summary>
    [DataType(DataType.MultilineText)]
    [AllowHtml]
    public string Text { get; set; }
}

Now as you noticed I don't provide the actual error message. I use conventions by Haacked to add the messages. It makes it simple to add localized validation rules.

It basically comes down to a recource file containing something like:

Test_Name = "Provide name"
Test_Name_Required = "Name is required"

And these messages and naming will be used when you call regular MVC view code like

<div class="editor-container">
    <div class="editor-label">
        @Html.LabelFor(model => model.Name) <!--"Provide name"-->
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.Name)
        @Html.ValidationMessageFor(model => model.Name) <!--"Name is required"-->
    </div>
</div>

Your second question, about different validation for add/edit can be handled in two ways. The best way, would be to use views as they are actually intended. That means you don't pass your actual models to the views, but you create a view model that contains only the data. So you have a view model for Create with the proper validation rules and a view model for Edit with the proper rules, and when they pass you insert the result in your actual model. This however requires a lot more code and manual work, so I can imagine you're not really willing to do it like this.

Another option would be to use conditional validation like explained by viperguynaz. Now instead of a boolean, my classes that require a change between edit/add have a primary key Id int. So I check if Id>0 to determine if it is an edit or not.

UPDATE:

If you want to update validation on every ajax call, you could use jQuery ajaxComplete. This will revalidate all forms after every ajax request.

$( document ).ajaxComplete(function() {
    $('form').each(function() {
        var $el = $(this);
        $el.data('validator', null); 
        $.validator.unobtrusive.parse($el);
    })
});

If this is something you want, depends on how often you receive a form via AJAX. If you have a lot of AJAX request, like polling a status every 10seconds, than you don't want this. If you have an occasional AJAX request, that mostly contains a form, then you could use it.

If your AJAX returns a form you want to validate, then yes, it is good practise to update the validation. But I guess a better question would be "Do I really need to send the form by AJAX?" AJAX is fun and useful, but it should be used with care and thought.

Wednesday, October 19, 2022
 
2

Late answer, but I want to rationalize on the subject:

Usually, in a web app, you want to send emails either as a direct reaction to a client. Or as a background task, in case we're talking about a newsletter/notification mail sort of thing.

The model is basically a data storage mapper. Its logic should encapsulate data-handling/communication with data storage handling. Therefore, inserting logic which does not relate to it is a bit tricky, and in most cases wrong. Let us take the example: User registers an account and should receive a confirmation email. In this case one could say, the confirmation email is a direct effect of the creation of a new account. Now, instead of doing it in the web app, try to create a user in the console. Sounds wrong to trigger a callback in that case, right? So, callback option scratched. Should we still write the method in the model? Well, if it's a direct effect of a user action/input, then it should stay in that workflow. I would write it in the controller after the user was successfully created. Directly. Replicating this logic in the model to be called in the controller anyways adds unnecessary modularity, and dependency of an Active Record model from Action Mailer. Try to consider sharing the model over many apps, in which some of them don't want Action Mailer for it. For the stated reasons, I'm of the opinion that the mailer calls should be where they make sense, and usually the model is not that place. Try to give me examples where it does make.

Saturday, August 20, 2022
 
px06
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :