Laravel 4 - 在其他控制器中使用控制器动作?

问题描述:

well I had a few controllers that were doing my jobs, now I created a few more controllers; some where in middle of an action in new controller, I just remembered that I've written the proper code in old controllers.

so for e.g. in oldController@handyAction

some_good_codez();

and in newController@newAction

$myOldController = Load::controller('oldController'); // I wish Laravel had this?
$myOldController->handyAction();

我有几个控制器在做我的工作,现在我创建了一些控制器; 在新控制器的动作中间的某些地方,我只记得我在旧控制器中编写了正确的代码。 p>

所以对于例如 在oldController @ handyAction p>

  some_good_codez(); 
  code>  pre> 
 
 

和newController @ newAction p> $ myOldController = Load :: controller('oldController'); //我希望Laravel有这个吗? $ myOldController-> handyAction(); code> pre> div>

This is wrong. Controllers have to have just one responsibility: receive a http request, get some data from your models and pass it away to a view. If your controllers have to 'talk' with another controller, it's probably because you're processing data on them, or reading data through them and you shouldn't because to process data is the responsibility of models not controllers.

First, take a look at SOLID and Single responsibility principle. If you can buy Taylor Otwell's book: Laravel: From Apprentice To Artisan, do it, it will give you a good idea of how to implement SOLID principles in Laravel.

Then, if you really need to use a method on two different controllers, create a new class and use this class on both controllers. Just refactor your code to do something like this:

class MyController1 extends Controller {

    public function __construct(MyClass $class)   
    {
        $this->myClass = $class;
    }

    public function store()
    {
        $this->myClass->doWhatever();
    }

}

class MyController2 extends Controller {

    public function __construct(MyClass $class)   
    {
        $this->myClass = $class;
    }

    public function update()
    {
        $this->myClass->doWhatever();
    }

}

class MyClass {

    public function doWhatever()
    {
        return 'done';
    }

}

This is still wrong, but a little better.

What's even better is to process your data in a data repository, so take a look at the Repository Pattern, this Taylor's video will give you a nice idea of how you do it. It talks about testability, but it's not just that, you can create domain repositories and mix two or more models inside it and process data from those models in one class. A code using the Repository pattern would looke more like:

Create an interface (contract):

interface MyDataRepository {

    public function getInfoA();
    public function getInfoB($dataA);

}

Create a class that implements this interface

class MyData implements MyDataRepository {

    public function __construct(MyModelA $dataA, MyModelB $dataB)   
    {
        $this->dataA = $dataA;
        $this->dataB = $dataB;
    }

    public function getInfoA()
    {
        return $this->dataA->processData();
    }

    public function getInfoB($dataA)
    {
        return $this->dataB->processData( $this->getInfoA() );
    }

}

[edit]

Think of a repository as a bag, where you can put everything you need and extract what you need from it.

In this repository we are using 2 models (MyModelA e MyModelB), instantiated by the Laravel's IoC Container, and in the method getInfoB the repository is using both to generate data and pass it back to the controller.

This all come from Domain Driven Development. A Domain is a complex model, like Order, that's built using data from orders, users, items, shippings and payments tables. Without all this information you don't really have an Order to show on your payment page, so you create an Order Repository and mix all your ORM in one class, wich has the single resposibility to get data from those tables and send it back to your controller.

[/edit]

You'll have to tell Laravel to instantiate that class when it needs an instance of your Interface:

App::bind('MyDataRepository', 'MyData ');

This is done only when you are using interfaces, here we are doing the same using a concrete class and we don't need to inform anything, Laravel knows what to do:

public function __construct(MyClass $class)   
{
    $this->myClass = $class;
}

And create your controllers using the repository:

    class MyController1 extends Controller {

    public function __construct(MyDataRepository $data)   
    {
        $this->data = $data;
    }

    public function store()
    {
        $this->data->getInfoA();
    }

}

class MyController2 extends Controller {

    public function __construct(MyDataRepository $class)   
    {
        $this->data = $data;
    }

    public function update()
    {
        $this->myClass->getInfoB();
    }

}

Laravel will automatically instantiate an instance of your class, you don't need to do anything else to make it work.