Injecting SQL Tables in Laminas / Zend Framework - is this the correct way?

I probably have some understanding problem with Laminas, or maybe I’m just too complicated :slight_smile:
hopefully someone can bring light in this…

I have an IndexController, an IndexController Factory and 2 Tables (User, Photos).

The Tables are all extensions of the AbstractTableGateway:

UserTable.php

<?php

declare(strict_types=1);

namespace Slideshow\Model\Table;

use Laminas\Db\Adapter\Adapter;

class UserTable extends AbstractTableGateway
{
    protected $adapter;
    protected $table = 'user';

    public function __construct(Adapter $adapter)
    {
        $this->adapter = $adapter;
        $this->initialize();
    }

    public function getUsersThatHaveAGallery()
    {
         // sql... select... from... where.....
    }
}

PhotosTable.php

<?php

declare(strict_types=1);

namespace Slideshow\Model\Table;

use Laminas\Db\Adapter\Adapter;

class UserTable extends AbstractTableGateway
{
    protected $adapter;
    protected $table = 'photos';

    public function __construct(Adapter $adapter)
    {
        $this->adapter = $adapter;
        $this->initialize();
    }

    public function getPhotosFromUser($user_id)
    {
         // sql...select photos from user where id = ....
    }
}

In my IndexController I’m creating an Instance of an Gallery-Class which needs the above mentioned UserTable and PhotosTable.

Is the correct way now to inject the UserTable and PhotoTable in the IndexControllerFactory.php and from the IndexController.php inject them both to the Gallery Class like this:

IndexControllerFactory.php

<?php

declare(strict_types=1);

namespace Slideshow\Controller\Factory;

use Slideshow\Model\Table\UserTable;
use Slideshow\Model\Table\PhotosTable;
use Interop\Container\ContainerInterface;
use Laminas\ServiceManager\Factory\FactoryInterface;


class IndexControllerFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestName, array $options = null)
    {
        return new IndexController(
            $container->get(UserTable::class),
            $container->get(PhotosTable::class),
            $container->get('ApplicationConfig')
        );
    }
}

IndexController.php


<?php

declare(strict_types=1);

namespace Slideshow\Controller;

use Slideshow\Model\Table\UserTable;
use Slideshow\Model\Table\PhotosTable;
use Laminas\Mvc\Controller\AbstractActionController;
use Laminas\View\Model\ViewModel;

class IndexController extends AbstractActionController
{
    private $userTable;
    private $photosTable;
    private $config;

    public function __construct(UserTable $userTable, PhotosTable $photosTable, array $config)
    {
        $this->userTable = $userTable;
        $this->photosTable = $photosTable;
        $this->config = $config;
    }

    public function indexAction()
    {
        // At this Point now I have to inject the Tables again ? 
        $Gallery = new Gallery($this->userTable, $this->photosTable);

        $html = $Gallery->renderUserListHtml();
        
        var_dump($html);
    }
}


Gallery.php

class Gallery {

   private $userTable;
   private $photosTable;

   // At this point I have to inject the tables AGAIN to make them usable in the Gallery.php ?
   public function __construct(UserTable $userTable, PhotosTable $photosTable)
   {
        $this->userTable = $userTable;
        $this->photosTable = $photosTable;
    {
 
   public function renderUserListHtml()
   {
      $sqlResult = $this->userTable->getUsersThatHaveAGallery();

      foreach($sqlResult as $k => $v) {
            $html .= '<div>' . $v['user_name'] . '</div><br />';
      }
 
      return $html;
}

So my main question is: is the above written code correct ?
is it really necessary to inject the tables

  1. from Factory to IndexController
  2. from IndexController to Gallery Class

until I can finally “use them” in the methods of the gallery class.

It seems to be a lot of code until I finally can use them !?

Would not be better to have Gallery class as ViewHelper? Than you can pass services in ViewHelper constructor.

Or consider SQL JOIN or SQL view (if you dont need to call modules separately in controller).

When I have a module I usually crate service class … Slideshow with setters and getters for module forms, models etc. In controller construct I just pass this service class Slideshow and than acces models or forms from service class.

Controller action example …

public function updateHourAction()
    {
        $orderId = (int) $this->params()->fromRoute('id');
        $request = $this->getRequest();
        try {
            $this->serviceOrder->updateHour($request->getPost());
            $this->flashMessenger()->addSuccessMessage('sssss');
        } catch (\Throwable $e) {
            $this->getEventManager()->trigger('log', $this,
                array(
                    'priority' => Logger::ERR,
                    'message' => __METHOD__ . '->' . $e->getMessage()
                ));
            $this->flashMessenger()->addErrorMessage('eeeee');
        }

        return $this->redirect()->toRoute('rrrrr', array(
            'id' => $orderId
        ));
    }

Service method updateHour

    /**
     * @param \ArrayObject $post
     * @return number
     */
    public function updateHour($post)
    {
        $entity = new \OrderFront\Entity\Order();
        $entity->exchangeArray($post);
       
        return $this->getModelOrder()->update($entity);
    }
1 Like

This may just be a style thing and I’m no expert, but here’s what I do in situations like this (which I deal with all the time).

  • Create a GalleryFactory class that injects the UserTable and PhotosTable (just like you are doing in the IndexControllerFactory now).
    • you will need to add an entry in module.config.php in the service_manager section that tells the framework that when you need a Gallery object to use GalleryFactory::class.

  • Modify the IndexControllerFactory:
    • remove the UserTableandPhotosTable`
    • add $container->get(Gallery::class); // inject the Gallery object.

  • Modify IndexController
    • add a new class variable private $gallery;
    • add code to the __construct to accept the Gallery class and assign it to your new class variable.
    • Now you can remove this line:
      $Gallery = new Gallery($this->userTable, $this->photosTable);
    • Then, in your controller, wherever you are currently accessing $Gallery, you’d just access $this->gallery. For example:
      $html = $this->gallery->renderUserListHtml();

Now, this means that you won’t be able to access the UserTable or PhotosTable from your IndexController, but I never access tables in the Controller unless it is a super-simple controller (with no other associated models).

Hope that helps!