Redirect from widget

Hi all,

I’d like to talk about application design and MVC.

Suppose I have a form that can appear on many site pages. For example, Contact form, or Login form.

In order to keep it DRY, I’m thinking about creating a widget for this form and doing all the processing in this widget.

I have two questions:

  1. Is it OK (from the point of app design and MVC) to make create/update operations in the widget?

  2. Is it OK to call a redirect from the widget?

PS. Yeah, I know that I can use AJAX and partials for this, but the question is about widgets.

If you want to be really MVC, no. This is because a widget is a type of reusable view or view component. Redirects (and any routing logic) are the job of the controller and the create/update operations are the job of the model.

You could just write an accompanying model for these widgets and pass them when you call the widget from in your view.

Sometimes we need to deviate from one convention to meet another, in this case DRY over MVC. If both really matter to you, I’m sure you can engineer a solution.

Definitely not ok. How are you going to perform create/update operations inside a widget? Check if $_POST/$_GET variables exist? But what about access control and action filters (postOnly, ajaxOnly)?

You should create a LoginController and ContactFormController. I don’t see how it can brake the DRY principle, I would say it follows the KISS principle :)

We do the same in controller, right?


if (isset($_POST['MyModel'])) {

    $model->attributes = $_POST['MyModel'];

    ....

}



Again, in controller we do the same thing. Take a look at actionCreate, for example.

Ok, let’s consider the following situation.

User opens "list" page, i.e.


function actionIndex() {

    $items = MyModel::model()->findAll();

    $this->render('index', array('items' => $items));

}



I want to add contact form at the end of the list.

In order to keep DRY I should use partials or widgets for this.

Let’s suppose I’ve created a separate controller (ContactController) for processing submitted form.

Everything goes well until user enters invalid data in this form.

We must display pre-filled contact form with errors.

How should we do it?

But CWidget doesn’t have accessRules() and filters() methods. I just want to say Yii’s CWidget is not supposed to replace a controller, there were some discussions about it already but with no result.

I used to solve this problem with ajax validation. On some sites user is redirected to a separate login page if he enters invalid data.

If you are not fine with ajax validation, then I don’t see a better (easier) solution than to put everything inside a widget, but, as I said above, widgets are not supposed to perform such operations, they are just to show some content. For example, we don’t use CActiveForm to validate and save models, we just use it to render an html form.

Edit: related discussion, one more.

Yes, I’m using ajax too for now.

My question is rather philosophic than real.

Thanks, I’ll have a look.

You are free to share your suggestions to make the dream real :D I like the idea of self-contained widgets, but currently it’s not possible.

As I understand, you want to reuse the login and contact which are very common desire among us.

DRY and KISS should come together and therefore you may want to just make the code that handle the logic of login as well as the code that handle the form submission data in a contact form to be reusable. In term of MCV, make the ‘M’ reusable. The answer to your 2 questions at the original post are YES but doing a widget will violate the KISS. A widget needs its companion view, and when you have 2 files in for something you want to be reusable, is not simple. You will find out that dealing the the CSS, making the the HTML generated code in that view is deadly hard and at the end, you your next project you will end up with modifying the views.

This is additional to the problem of filler has been mentioned. However, for my applications it’s not the problem we can solve.

Yes, and many more. For example, comment form.

So does extra controller.

For example, I may have CommentWidget + its view, OR CommentController + its view (partial?..).

But in case of controller, you will have to put form creation code in EVERY controller that displays form.

I mean, these lines:


$commentForm = new Comment;

$this->render('index', array('form' => $commentForm, ...));

Someone was talking about KISS? :)

Angel, this is a kind of practical approach for reusable code. It needs time for the right answer. My suggestion is just try to keep the model class reusable, assume that you keep the code that handle login or comment / contact form in there.

Yes, that’s too simple and not really much reused but it’s applied to more scenario than login or contact form. You can of course spend weeks or months to make a nice and highly customizable login widget or contact widget but it’s not productive. Look at all of the projects you did, if you’ve already made it then do the same thing in Yii (then share it here pls). Otherwise, Yii does not help you to do any magic here.

You could perhaps extend your Controller to catch $_POST messages sent by a widget with its name let’s say(e.g. WidgetName) and then call


WidgetName::postProcess();

which would do the actual redirect. To avoid strong coupling you could pass the URL for redirection and any model that you want to use via the widget properties. I think that with this approach not only you have the benefits of accessRules(),filters() and other controller goodies but also a reusable loosely coupled widget. For added security, you should check whether the posted widget name exists and perhaps the csrf.