Giter Site home page Giter Site logo

Comments (11)

samdark avatar samdark commented on July 26, 2024 4

From my practice trying to apply DRY to controllers isn't that good idea in general. Despite being similar at the start they're often becoming too different in the development process. Code becomes complicated because of it. If there's a common part it worth moving it to a separate class that's not controller.

In Yii you can return data that's obtained not only from a View but a string or an array of data or just int return code (in case of console controllers):

public function actionTest()
{
    return 'test';
}

from yii-core.

fernandezekiel avatar fernandezekiel commented on July 26, 2024 1

you might actually want to implement the RestController's style of handling the data if it is meant to be overriden

from yii-core.

samdark avatar samdark commented on July 26, 2024 1

It's all different now.

from yii-core.

samdark avatar samdark commented on July 26, 2024

Could you give concrete example?

from yii-core.

nkostadinov avatar nkostadinov commented on July 26, 2024

Ok, I have a CrudController which has basic functionalities for CRUD operation on a model class.

class CrudController extends Controller
{
    public function actionIndex()
    {
        $dataProvider = new ActiveDataProvider([
            'query' => call_user_func([$this->modelClass, 'find']),
        ]);        
       return $this->render($this->indexView, compact('dataProvider'));
    }
}

So now I want to use my CrudController, but want to add another parameter to the view for a filter dropdown for example. So I want to do something like this:

UserController extends CrudController 
{
    public function actionIndex() //Override the base class
    {
        $view = parent::actionIndex();  //The problem is that a rendered string is returned !
        //change some parameters in the view->params or change the view file
        $view->params['comboData'] = $this->getComboData();
        $view->viewFile = 'new_view';
        //
        return $view; //the actual view should be rendered in the caller function
    }
}

It is a general OOP principle to be able to extend a class/method and change the way it works and its return result. Which makes it hard here because of the string returned. In this same case on a rest controller an array/object is returned which can be manipulated which is OK.

from yii-core.

nkostadinov avatar nkostadinov commented on July 26, 2024

@samdark overriding controllers can become a burden but that really depends on who is doing it :) and the framework shouldn't stop you. The View class itself should be generalized enough to take strings, arrays and so on and the renderer at the end of the loop should decide how to transform it.

I think that a real OOP MVC should work with view objects e.g. $this->render('view') should return a View instance with prefilled params and viewfile properties so it can be easily overridden and later on a renderer class should generate content based on the view object.

If someone is wondering I have currently worked around this issue like this :

    public function actionIndex()
    {
        return $this->render($this->indexView, $this->getIndexData());
    }

    protected function getIndexData()
    {
        $dataProvider = new ActiveDataProvider([
            'query' => call_user_func([$this->modelClass, 'find']),
        ]);
        return compact('dataProvider');
    }

The getIndexData() method can be easily extended.

from yii-core.

fernandezekiel avatar fernandezekiel commented on July 26, 2024

@nkostadinov that's how i do it in my rest api, but it returns the active query object instead

from yii-core.

AnatolyRugalev avatar AnatolyRugalev commented on July 26, 2024

I solved this problem once with using response formatter. It recognizes output array of action and renders result. Actual code:

namespace frontend\controllers\base;
use Yii;

class Controller extends \common\web\Controller
{
    public static $instance;

    public static $actionInstance;

    public function beforeAction($action)
    {
        self::$instance = $this;
        self::$actionInstance = $action;

        return parent::beforeAction($action);
    }
}

namespace frontend\components;

use frontend\controllers\base\Controller;
use Yii;
use yii\web\HtmlResponseFormatter;
use yii\web\Response;

class RenderFormatter extends HtmlResponseFormatter
{

    public function format($response)
    {
        if ($response->content) {
            return;
        }
        if (is_array($response->data) && isset(Controller::$instance) && isset(Controller::$actionInstance)) {
            $data = $response->data;
            Controller::$instance->action = Controller::$actionInstance;
            Yii::$app->controller = Controller::$instance;
            if (!isset($data['params'])) {
                $data = ['params' => $data];
            }
            if (!isset($data['viewFile'])) {
                $data['viewFile'] = Controller::$actionInstance->id;
            }
            if (!isset($data['render']) || !in_array($data['render'], ['render', 'renderAjax', 'renderPartial'])) {
                $data['render'] = 'render';
            }
            $response->content = Yii::$app->controller->{$data['render']}($data['viewFile'], $data['params']);
            Yii::$app->controller->action = null;
            Yii::$app->controller = null;
        } else {
            parent::format($response);
        }
        Controller::$actionInstance = null;
        Controller::$instance = null;
    }
}
  1. There is some hacks with controller and action instances
  2. This is ugly
  3. There is no really need to do that - I checked 😄 . I'm fully agree with @samdark

from yii-core.

samdark avatar samdark commented on July 26, 2024

@AnatolyRugalev yes, that's how I'd do it but same as you I've figured out about 3..

from yii-core.

nkostadinov avatar nkostadinov commented on July 26, 2024

Well I did it like this and for now. This is in my base controller :

    public function render($view, $params = [])
    {
        $return new View([
            'viewFile' => $view,
            'params' => $params,
        ]);
    }

    public function runAction($id, $params = [])
    {
        $result = parent::runAction($id, $params);
        if($result instanceof View) 
                $result = parent::render($result->viewFile, $result->params);
        return $result;
    }

from yii-core.

armpogart avatar armpogart commented on July 26, 2024

The same problem applies to overriding actions defined in parent Controller.
As an example if I have basic CRUD actions defined in parent controller (external actions), and I want to override just one of them in child with inline action, there's no way to do it.

Though I think it's a separate issue.

from yii-core.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.