ZF2 Is there a shorter/cleaner way to get the Tabl

2019-08-03 23:36发布

Recently I made my first ZF2 application. I was walking through the code to see if I could make the code somewhat cleaner. Then I noticed that my controller classes have a huge block of code that supplies the controller of the TableGateway classes it needs. And I wondered is there a shorter/cleaner way to do this? It just seems silly that half of my controller class is dedicated to this simple task of fetching some TableGateWay classes.

protected $appointmentTable;
protected $customerTable;
protected $serviceTable;
protected $locationTable;
// ... some action methods that actually do the work.
public function getAppointmentTable()
{
    if (!$this->appointmentTable) {
        $sm = $this->getServiceLocator();
        $this->appointmentTable = $sm->get('Appointment\Model\AppointmentTable');
    }
    return $this->appointmentTable;
}

public function getServiceTable()
{
    if (!$this->serviceTable) {
        $sm = $this->getServiceLocator();
        $this->serviceTable = $sm->get('Appointment\Model\ServiceTable');
    }
    return $this->serviceTable;
}

public function getLocationTable()
{
    if (!$this->locationTable) {
        $sm = $this->getServiceLocator();
        $this->locationTable = $sm->get('Appointment\Model\LocationTable');
    }
    return $this->locationTable;
}

public function getCustomerTable()
{
    if (!$this->customerTable) {
        $sm = $this->getServiceLocator();
        $this->customerTable = $sm->get('Customer\Model\CustomerTable');
    }
    return $this->customerTable;
}

2条回答
叛逆
2楼-- · 2019-08-04 00:04

Could you just simplify the process in another method? I'm not aware of this function in Zend2, but still, if there is no method on framework level, you can write your own simplified method

My test so far:

public function setTable($method) {
    $method = lcfirst(str_replace("get", "", $method));
    $this->$method = 'Apointment\Model\\'.ucfirst($method);
    return $this->$method;
}

public function getLocationTable() {
    $this->setTable(__FUNCTION__);
    var_dump(get_object_vars($this));
}

Outputs:

array (size=1)
  'locationTable' => string 'Apointment\Model\LocationTable' (length=30)

So you can change setTable() method to use your set() proxy:

public function setTable($method) {
    $method = lcfirst(str_replace("get", "", $method));
    if (!$this->$method) {
        $sm = $this->getServiceLocator();
        $this->$method = $sm->get('Apointment\Model\\'.ucfirst($method));
    }
    return $this->$method;
}

public function getLocationTable() {
    return $this->setTable(__FUNCTION__);
}

public function getServiceTable() {
    return $this->setTable(__FUNCTION__);
}

Or you can get all your tables in array, iterate through it and pass the name to your setTable() method, which will set inner properties.


My string test (because I don't have ZF2 right here, and testing if the proper string which you are passing to the set() proxy is built:

class Tables {

    public function setTable($method) {
        $method = lcfirst(str_replace("get", "", $method));
        $this->$method = 'Apointment\Model\\'.ucfirst($method);
            /*if (!$this->$method) {
            $sm = $this->getServiceLocator();
            $this->$method = $sm->get('Apointment\Model\\'.ucfirst($method));
            }*/
        return $this->$method;
    }

    public function getLocationTable() {
        return $this->locationTable;
    }

    public function getServiceTable() {
        return $this->serviceTable;
    }

    public function getAppointmentTable() {
        return $this->appointmentTable;
    }

    public function setAllTables() {
        foreach (get_class_methods(__CLASS__) as $method) {
            if (strpos($method, 'get')!== false && strpos($method, 'Table')!==false)
                $this->setTable($method);
        }
    }
}

$tables = new Tables();
$tables->setAllTables();
var_dump(get_object_vars(($tables)));

Outputs:

array (size=3)
  'locationTable' => string 'Apointment\Model\LocationTable' (length=30)
  'serviceTable' => string 'Apointment\Model\ServiceTable' (length=29)
  'appointmentTable' => string 'Apointment\Model\AppointmentTable' (length=33)

Now all your get____Table() methods are valid getters. E.g.:

var_dump($tables->getServiceTable());

returns

string 'Apointment\Model\ServiceTable' (length=29)
查看更多
成全新的幸福
3楼-- · 2019-08-04 00:14

The way your Controllers should ideally be set up is through the means of proper(!) dependency injection. In Zend Framework 2 you have two main ways to declare controllers within the ControllerManager. The first one being invokables for controllers who have no dependencies and the second one being factories for controllers who have dependencies.

Any TableGateway always is a dependency. To my experience there are no controllers who are invokables at all :P

There's two ways to set up controller factories.

  1. Module.php using getControllerConfig()
  2. Under the controllers[factories] key in your module.config.php using Factory-Classes

For simplicity I'll choose the first approach now:

public function getControllerConfig()
{
    return array(
        'factories' => array(
            'My\Foo\Controller' => function ($cpm) {
                //@var $cpm \Zend\Mvc\Controller\ControllerManager
                $serviceLocator = $cpm->getServiceLocator();
                $tableGateway   = $serviceLocator->get('My\Table\Gateway');

                return new \My\Foo\Controller($tableGateway);
            }
        )
    );
}

With this, all that's left is for you to modify your controller and have it pass the respective tablegateway inside its constructor:

class Controller
{
    protected $tableGateway;

    public function __construct(\My\Table\Gateway $tg)
    {
        $this->tableGateway = $tg;
    }

    public function indexAction()
    {
        return new ViewModel(array(
            'entries' => $this->tableGateway->select()
        ));
    }
}

And that's all there is to it. It's all about proper dependency injection that makes your life ultimately so much easier.

Obviously this example only covers one table, but you can do the same just passing more tables through the constructor. That is: only if you really need ALL TableGateways in there (which sounds a bit fishy) ;)

查看更多
登录 后发表回答