tomhre
tomhre

Reputation: 315

Zend Framework 2 Database Access in Models

I am developing my app in ZF2 but despite trying i could not find answer for my database/model problem.

I think of TableGateway that is pretty much the copy of skeleton application example as a AdminUser collection of some sorts that does the query tasks and returns AdminUser instance.

this is part of Module.php

public function getServiceConfig()
{

    return array(
        'factories' => array(
            'Admin\Model\AdminUserTable' =>  function($sm) {
                $tableGateway = $sm->get('AdminUserTableGateway');
                $table = new AdminUserTable($tableGateway);
                return $table;
            },
            'AdminUserTableGateway' => function ($sm) {
                $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                $resultSetPrototype = new ResultSet();
                $resultSetPrototype->setArrayObjectPrototype(new AdminUser());
                return new TableGateway('zxf_admin_user', $dbAdapter, null, $resultSetPrototype);
            },
        ),
    );
}

here is the controller

protected $adminUserTable;

public function indexAction()
{
    $this->layout('layout/admin');

    return new ViewModel(array(
        'text' => 'some text',
        'users' => $this->getAdminUserTable()->fetchAll(),
    ));
}

public function getAdminUserTable()
{
    if (!$this->adminUserTable) {
        $sm = $this->getServiceLocator();
        $this->adminUserTable = $sm->get('Admin\Model\AdminUserTable');
    }
    return $this->adminUserTable;
}

I am not sure of the getAdminUserTable approach,and this is the function that gets user

public function getAdminUser($id)
{
    $id  = (int) $id;
    $rowset = $this->tableGateway->select(array('id' => $id));
    $row = $rowset->current();
    if (!$row) {
        throw new \Exception("Could not find row $id");
    }
    return $row;
}

but what if I am interested in having another class something like AdminUserPriviliges that would be object that is returned when you do this in your controller

    $user = $this->getAdminUserTable()->getAdminUser($id);

to get privileges even though they are different class and data for them is stored in different table, hence the below method would need to perform database search but because inside AdminUser object it could use the $this->id and furthermore via AdminUser you would get access to privileges themselves so I could then get them on request as a collection of objects, something like this

    $user_piviliges = $user->getPriviliges();

and the content should be collection of AdminUserPriviliges instances

    return new ViewModel(array(
        'text' => 'some text',
        'user' => $this->getAdminUserTable()->getAdminUser($id);,
    ));

so sending user objects back to view would mean we have the privileges too. in a similar way when i move to other entities i would like to be able to do the same

like $product->getImages() would give you all images but they are not stored in the same table of course.

Is there a better way to do it or even simpler?

Upvotes: 3

Views: 2617

Answers (1)

GertG
GertG

Reputation: 979

Your getAdminUserTable and similar functions are doing work that the service manager already does for you. By default, each call to $sm->get('Admin\Model\AdminUserTable'); during one request will return the same object. For example, say I have this service:

public function getServiceConfig()
{    
    return array(
            'factories' => array(
                    'TestService' =>  function($sm) {
                        return time();
                    },
            ),
    );
}

and I use it in a controller action like this:

public function testAction()
{
    $sm = $this->getServiceLocator();
    $first = $sm->get('TestService');
    sleep(1);
    $second = $sm->get('TestService');

    echo $first;
    echo '<br />';
    echo $second;

    return $this->response;
}

If you execute that, you'll see that the two numbers printed are always the same. This is because both $first and $second are referring to the same object, that was created when you first called $sm->get('TestService');.

If you don't want that behavior, you need to specify that in the shared key of your config.

    'shared' => array(
            // Indicate services that should **NOT** be
            // shared -- i.e., ones where you want a different instance
            // every time.
            'TestService' => false,
    )

If I understand the rest of your question, it boils down to building your own domain objects and using those in the controller instead of directly using your table gateways in the controller. If so, yes, that's a very good idea for any site that does more than just representing your database data in a straightforward way (straightforward as in: one Film table translates to one film list page and one film detail page, with minimal extra logic).

Just be careful that you don't end up loading way more data than you need to. For example, a User will probably not be a very heavy object, and it might make sense to load all data of at least the current user into an object. But say you have a Product table that is linked to most of the other tables in your database (product info, shipping info, components...). You probably want to avoid putting all that info in an object if all you're really doing is presenting a list of product names.

Edited in response to comment

What are your issues then exactly? How to implement a method like getProfiles in a User class? You'd do something like

public function getPrivileges() {
    $privTable = new TableGateway('PRIVILEGES', $adapter);
    $rowset = $privTable->select(array('USER' => $this->id));
    $privileges = array();

    foreach ($rowset as $row) {
        $priv = new Privilege;
        $priv.setType($row['TYPE']);
        //...set other properties
        $privileges[] = $priv;
    }

    return $privileges;
}

Or if you need to get info from multiple tables, you don't use a TableGateway but construct your own Zend Sql/Select object, where you're free to add any joins, wheres etc. See http://zf2.readthedocs.org/en/latest/modules/zend.db.sql.html.

Upvotes: 2

Related Questions