Luka
Luka

Reputation: 758

Zend Framework 2: Pass Variable to View Helper

I have created a View Helper to display latest Adverts from a Database Table. Since I have different Types of Adverts, I would like to be able to pass a variable from inside my View where I call the View Helper to show specific Adverts.

I am sorry that I can not explain it in a better way, but I am still a total beginner in ZF2. I will add the Sourcecode and hopefully this will make it more clear. Please note that I have the Sourcecode from a Book which displayed Pizza's randomly and changed it till it worked to show my adverts. I might still have Code in it which is not actually needed, so please do not wonder... Okay here the code:

1. the view: index.html

<?php foreach ($this->latestAdvert() as $value){ ?>
      <li><?php echo $value->getAdvertTitle();?></li>
<?php }?>

2. the view Helper: Advert\View\Helper\LatestAdvert.php

namespace Advert\View\Helper;
use Zend\View\Helper\AbstractHelper;

class LatestAdvert extends AbstractHelper
{

   protected $random = null;


   public function __construct($random)
   {
     $this->setLatestAdvert($random);
   }


   public function setLatestAdvert($random)
   {
     $this->random = $random;
   }


   public function getLatestAdvert()
   {
     return $this->random;
   }


   public function __invoke()
   {
     $latestAdverts = $this->getLatestAdvert();
     return $latestAdverts;
   }
}

3. the Factory: Advert\View\Helper\LatestAdvertFactory.php

namespace Advert\View\Helper;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class LatestAdvertFactory implements FactoryInterface
{

    public function createService(ServiceLocatorInterface $serviceLocator) 
    {
      $locator = $serviceLocator->getServiceLocator();
      $service = $locator->get('Advert\Service');
      $random  = $service->fetchSingleByRandom();
      $helper  = new LatestAdvert($random);

      return $helper;
    }
}

4. the Service: Advert\Service\LatestAdvertService .php

namespace Advert\Service;

use Advert\Entity\Advert as AdvertEntity;
use Doctrine\ORM\EntityManager;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\Debug\Debug;

class LatestAdvertService implements ServiceManagerAwareInterface
{

  /**
   * Service manager.
   * @var Zend\ServiceManager\ServiceManager
   */
   private $serviceManager = null;

  /**
   * Sets service manager.
   * @param Zend\ServiceManager\ServiceManager $serviceManager Service manager.
   */
  public function setServiceManager(ServiceManager $serviceManager)
  {
    $this->serviceManager = $serviceManager;
  }

  /**
   * Returns service manager.
   * @return type
   */
  public function getServiceLocator()
  {
    return $this->serviceManager;
  }


  public function fetchSingleByRandom()
  {     
    // Get Doctrine entity manager.
    $entityManager =  $this->getServiceLocator()
                           ->get('doctrine.entitymanager.orm_default');
    $advertType = 'wanted'; // This should be removed
    $random = $entityManager->getRepository('Advert\Entity\Advert')
                            ->findAdvertsByDate($advertType);

    return $random; 
  }
}

5. Module: Advert\Module.php

public function getServiceConfig()
{
    return array(
        'invokables' => array(
                 'Advert\Service' => 'Advert\Service\LatestAdvertService',
                    ),
    );
}

public function getViewHelperConfig()
{
    return array(
        'factories' => array(           
            'latestAdvert' => 'Advert\View\Helper\LatestAdvertFactory',
        ),

    );
}

As you can see in #4 I have a Variable called $advertType. I want to set the variable when I call the view Helper in my index.html, f.e. $this->latestAdvert('wanted'), but how can I pass this variable through all my files? I just can not find a solution for it. Does anyone got a tip for me how to achieve it? Thank you very much in advance.

!UPDATE!

As SenseException pointed out below, that injecting a service locator into a service is a bad practice and instead I should either inject repository or entity manager into the service, I have now worked out the first working solution for the entity manager.

For that I have updated 2 Files: module.php and LatestAdvertService.php

#5 module.php

public function getServiceConfig()
{
    return array(
            'factories' => array(

                    'Advert\Service' => function ($sl) {
                        $entityManager =   $sl->get('doctrine.entitymanager.orm_default');
                        $myService = new Service\LatestAdvertService();
                        $myService->setEntityManager($entityManager);
                        //or you can set repository
                        //$repository = $entityManager->getRepository('Advert\Entity\Advert');
                        //$myService->setRepository($repository);
                        return $myService;
                    },

4. the Service: Advert\Service\LatestAdvertService .php

namespace Advert\Service;

use Advert\Entity\Advert as AdvertEntity;
use Doctrine\ORM\EntityManager;

class LatestAdvertService 
{


  public function setEntityManager(EntityManager $entityManager)
  {
    $this->entityManager = $entityManager;
  }

  public function setRepository(Repository $repository) {
   $this->repository = $repository;
  }


  public function fetchSingleByAdvertType($advertType)
  {

    $random = $this->entityManager->getRepository('Advert\Entity\Advert')->findAdvertsByDate($advertType);  
//  $random = $this->repository->findAdvertsByDate($advertType);

    return $random;
  }
}

I have tried to inject the repository but get the following error message:

Argument 1 passed to Advert\Service\LatestAdvertService::setRepository() must be an instance of Advert\Service\AdvertRepository, instance of Advert\Repository\AdvertRepository given, called in

I will continue to find a solution for the repository injection and update when successful.

Upvotes: 0

Views: 992

Answers (1)

SenseException
SenseException

Reputation: 1075

How about this solution:

namespace Advert\View\Helper;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class LatestAdvertFactory implements FactoryInterface
{

    public function createService(ServiceLocatorInterface $serviceLocator) 
    {
      $locator = $serviceLocator->getServiceLocator();
      $service = $locator->get('Advert\Service');
      $helper  = new LatestAdvert($service);

      return $helper;
    }
}

And of course the helper class:

namespace Advert\View\Helper;
use Zend\View\Helper\AbstractHelper;

class LatestAdvert extends AbstractHelper
{

   protected $service;


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

   public function __invoke($advertType)
   {
       $latestAdverts = $this->service->fetchSingleByAdvertType($advertType);
       return $latestAdverts;
   }
}

And for the service:

public function fetchSingleByAdvertType($advertType)
{
    $entityManager =  $this->getServiceLocator()
                           ->get('doctrine.entitymanager.orm_default');

    $random = $entityManager->getRepository('Advert\Entity\Advert')
                            ->findAdvertsByDate($advertType);

    return $random; 
}

I tried to keep your code as close to your original as possible but please hear some suggestions about the service locator. It is a bad practice to inject a service locator into a service like you did in LatestAdvertService. Since you only need a repository for your service, just inject that one into it. If you need the entity manager in your service, inject it instead. Your unittests will thank you.

Upvotes: 2

Related Questions