Symfony 3 - difference between 2 arraycollection - diff

Is there a way to differentiate 2 ArrayCollection ? (like array_diff)
today I loop on the first and check if $it->contains() match, but I think it can be refactored.

You can use array_diff in the following way:
$diff = array_diff($arrayCollection1->toArray(), $arrayCollection2->toArray());
$arrayCollectionDiff = new ArrayCollection($diff);

I would suggest the following:
A class made of two functions:
function getElementFromANotInB gives the elements from ArrayCollection A not in ArrayCollection B (can also be used the other way around to get elements from B not in A);
function getElementCommonInAAndB gives common elements of ArrayCollection A and B.
Here is the class:
<?php
namespace [Bundle name]\DependencyInjection\ToolBox\ArrayCollectionTB;
use Doctrine\Common\Collections\ArrayCollection;
class ArrayCollectionTB {
public function __construct(){}
public function getElementFromANotInB(ArrayCollection $acA,ArrayCollection $acB){
return $elementsInANotInB = $acA->filter(function($a) use ($acB) {
return $acB->contains($a)===false;
});
}
public function getElementCommonInAAndB(ArrayCollection $acA,ArrayCollection $acB){
return $elementsCommonInAAndB = $acA->filter(function($a) use ($acB) {
return $acB->contains($a)===true;
});
}
}
?>
Here is the test class that comes with it:
<?php
namespace Tests\[Bundle name]\DependencyInjection\ToolBox\ArrayCollectionTB;
use [Bundle name]\DependencyInjection\ToolBox\ArrayCollectionTB\ArrayCollectionTB;
use Doctrine\Common\Collections\ArrayCollection;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
/**
* #coversDefaultClass [Bundle name]\DependencyInjection\ToolBox\ArrayCollectionTB
*/
class ArrayCollectionTBTest extends KernelTestCase{
private static $acA;
private static $acB;
private static $acTB;
public static function setUpBeforeClass()
{
//start the symfony kernel
$kernel = static::createKernel();
$kernel->boot();
//get the DI container
$container = $kernel->getContainer();
self::$acA = new ArrayCollection();
self::$acB = new ArrayCollection();
//acA and acB have in common 1,5
//acA has 2 and 3 not in acB
self::$acA->add('element 1');
self::$acA->add('element 2');
self::$acA->add('element 3');
self::$acA->add('element 5');
//acB has 4 and 6 not in acA
self::$acB->add('element 1');
self::$acB->add('element 4');
self::$acB->add('element 5');
self::$acB->add('element 6');
self::$acTB = new ArrayCollectionTB();
}
/**
* #covers ::getElementFromANotInB
* #testdox test check dif give element from ArrayCollection A not in ArrayCollection B
*/
public function testGetElementFromANotInB(){
$res = self::$acTB->getElementFromANotInB(self::$acA,self::$acB);
//result should be 2 and 3
$this->assertNotContains('element 1',$res);
$this->assertContains('element 2',$res);
$this->assertContains('element 3',$res);
$this->assertNotContains('element 4',$res);
$this->assertNotContains('element 5',$res);
$this->assertNotContains('element 6',$res);
}
/**
* #covers ::getElementFromANotInB
* #testdox test check dif give element from ArrayCollection B not in ArrayCollection A
*/
public function testGetElementFromBNotInA(){
$res = self::$acTB->getElementFromANotInB(self::$acB,self::$acA);
$this->assertNotContains('element 1',$res);
$this->assertNotContains('element 2',$res);
$this->assertNotContains('element 3',$res);
$this->assertContains('element 4',$res);
$this->assertNotContains('element 5',$res);
$this->assertContains('element 6',$res);
}
/**
* #covers ::getElementCommonInAAndB
* #testdox test check gives element from ArrayCollection A and ArrayCollection A
*/
public function testGetElementFromBAndA(){
$res = self::$acTB->getElementCommonInAAndB(self::$acB,self::$acA);
$this->assertContains('element 1',$res);
$this->assertNotContains('element 2',$res);
$this->assertNotContains('element 3',$res);
$this->assertNotContains('element 4',$res);
$this->assertContains('element 5',$res);
$this->assertNotContains('element 6',$res);
}
}
?>

When ArrayCollection is an array of objects:
$collFoo = ArrayCollection($arrayOfFooObjects);
$collBar = ArrayCollection($arrayOfBarObjects);
$diff = $collFoo->filter(function (Foo $fooObject) use ($collBar) {
return !$collBar->contains($fooObject);
});
Simple array_diff throws an error: Error: Object of class Foo could not be converted to string

Related

After updating Laravel to version 9, I can't sign an email inside a Mailable class

After updating Laravel to version 9, I can't sign an email inside a Mailable class.
my code in Laravel 6 worked:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class NotificationEmail extends Mailable
{
use Queueable, SerializesModels;
protected $person;
protected $data = [];
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($person, $data)
{
$this->person = $person;
$this->data = $data;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
return $this->view('mail.notification.senat')
->replyTo('noreply#example.com')
->subject('Test subject')
->with([
'person' => $this->person,
'data' => $this->data
])
->withSwiftMessage(function ($message){
$smimeSigner = new \Swift_Signers_SMimeSigner(
config('mail.sign_cert'),
[config('mail.sign_key'), config('mail.sign_key_password')]
);
$message->attachSigner($smimeSigner);
});
}
}
my code in Laravel 9 does not work:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Symfony\Component\Mime\Crypto\SMimeSigner;
use Symfony\Component\Mime\Email;
use Illuminate\Support\Facades\Log;
class NotificationEmail extends Mailable
{
use Queueable, SerializesModels;
protected $person;
protected $data = [];
/**
* Create a new message instance.
*
* #return void
*/
public function __construct($person, $data)
{
$this->person = $person;
$this->data = $data;
}
/**
* Build the message.
*
* #return $this
*/
public function build(): NotificationEmail {
return $this->view('mail.notification.senat')
->replyTo('noreply#example.com')
->subject('Test subject')
->with([
'person' => $this->person,
'data' => $this->data
])
->withSymfonyMessage(function (Email $message){
$certPath = storage_path(env('MAIL_SIGN_CERT'));
$keyPath = storage_path(env('MAIL_SIGN_KEY'));
// see: https://symfony.com/doc/current/mailer.html#signing-and-encrypting-messages
$signer = new SMimeSigner($certPath, $keyPath, env('MAIL_SIGN_KEY_PASSWORD'));
$signer->sign($message);
})
;
}
}
I know that Laravel 9 works with Symfony Mailer. However, the description does not use a Laravel Mailable environment.... See: https://symfony.com/doc/current/mailer.html#signing-and-encrypting-messages
The Laravel9 doc shows the way to customize: https://laravel.com/docs/9.x/mail#customizing-the-symfony-message
But my solution does not work. I get an error, line 48 "$signer->sign($message);"
A message must have a text or an HTML part or attachments.
Do you have a clue and can you help?
I would suggest an event listener for any outbound mail to achieve signing for all outbound mails with Laravel 9 / Symfony Mailer.
<?php
namespace App\Listeners\Mail;
use Illuminate\Mail\Events\MessageSending;
use Symfony\Component\Mime\Crypto\SMimeSigner;
use Symfony\Component\Mime\Email;
class MessageSendingListener
{
public function handle(MessageSending $event)
{
$signer = new SMimeSigner('/path/to/certificate.pem', 'path/to/private.key');
$signedMessage = $signer->sign($event->message);
$event->message->setHeaders($signedMessage->getHeaders());
$event->message->setBody($signedMessage->getBody());
}
}
Full explanation with the Event Service Provider:
https://laracasts.com/discuss/channels/laravel/laravel-9-outbound-smime-messages
Symfony Docs S/MIME:
https://symfony.com/doc/current/mailer.html#s-mime-signer
Cheers :)

Symfony forms -- removing an optional one-to-one relationship in an embedded form

I'm trying to use Symfony Forms to edit an existing entity that has a one-to-one relationship with another entity that is optional. I want to make it so that if it receives nothing for the associated entity, it will delete the entity.
Here's the gist of the code I've written:
class AType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('BType', new BType(), ['required' => false]);
}
}
The A entity's association with B:
class A
{
/**
* #ORM\OneToOne(
* targetEntity="B",
* mappedBy="a",
* cascade={"persist", "remove"},
* orphanRemoval=true
* )
* #Assert\Valid()
*/
private $b;
....
When I try binding the form with the data, even though nothing was submitted for B, it'll make validation errors that B's fields are blank, because there is an existing B entity associated with A.
I've also looked at FormEvents, but I have not been able to find a way to fix the problem that way either -- example of what I've tried adding to AType's buildForm:
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
$a = $event->getData();
if($a->getType() != 'Package') {
$a->setB(null);
}
});
Even with this additional code, the errors regarding B are still appearing.
Update:
I think I've gotten closer, but though I can tell it's getting into the new code correctly, the B entity still doesn't ultimately get deleted, I think because despite the $a->setB(null), for $a = $form->getData(); in the controller, $a->getB(); still returns data (i.e. editing the entity in AType's POST_SUBMIT seems to only change that data locally?)
New Code:
$builder->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) {
$data = $event->getData();
if(!isset($data['B'])) {
$event->getForm()->remove('B');
}
});
$builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
$a = $event->getData();
if(!$event->getForm()->has('B') && $a->getB()) {
$a->setB(null);
}
});
I'm really at a loss because while $a->setB(null) does not perpetuate to the controller, if I create a property $a->test and set that in the same location, that does perpetuate to the controller.
Update 2
As per comments, I'm adding some code from Entity B and the controller:
class B
{
/**
* #var \A
*
* #ORM\GeneratedValue(strategy="NONE")
* #ORM\OneToOne(targetEntity="A", inversedBy="b", cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="a_id", referencedColumnName="id")
* })
*/
private $a;
As for the controller, it gets more complicated as I'm actually dealing with a 2 deep relation: C hasMany A hasOne B. It's also not using form inputs, but rather formatted json. I'm using http://jmsyst.com/bundles/JMSSerializerBundle/master/installation to serialize the output back into json, as well as a helper function to serialize any error messages.
//Where $c is either a new C entity or an existing C entity to be edited
private function handleForm($c)
{
$json = $this->getJsonFromRequest();
if (false === $json) {
throw new \Exception('Invalid JSON');
}
$form = $this->createForm(new CType(), $c);
$em = $this->getDoctrine()->getEntityManager();
if($form->bind(json_decode($json, true)) && $form->isValid()) {
$c = $form->getData();
...
$em->persist($c);
$em->flush();
$response = new Response($this->getSerializer()->serialize($c, 'json'));
$response->headers->set('Content-Type', 'application/json');
return $response;
} else {
$response = array('code' => 'invalid', 'details' => $this->getErrorMessages($form));
return new JsonResponse($response, 400);
}
}
class C
{
/**
* #ORM\OneToMany(targetEntity="A", mappedBy="c", cascade={"persist", "remove"}, indexBy="id")
* #Assert\Valid()
* #Serializer\Expose
*/
protected $a;
A's relationship to C
/**
* #var \C
*
* #ORM\ManyToOne(targetEntity="C", inversedBy="a")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="c_id", referencedColumnName="id")
* })
* #Assert\NotBlank
*/
private $c;
And CType
class CType extends AbstractType
{
$builder->add(
'a',
'collection',
array(
'type' => new AType(),
'by_reference' => false,
'allow_add' => true,
'allow_delete' => true
)
);

Doctrine ODM with MongoDB need both reference mappings set

I'm having the following situation in doctrine ODM with MongoDB and Symfony 2.x
Class A:
class Port extends AbstractEthernetPort
{
/** Other Fields **/
/**
* Owning the reference
* #MongoDB\ReferenceOne(
* targetDocument="\xxx\AbstractObject",
* cascade="all",
* inversedBy="ports"
* )
*/
protected $device;
/** SETTER and GETTERS **/
}
Class B:
class Device extends AbstractObject
{
/** Other Fields **/
/**
* #MongoDB\ReferenceMany(
* targetDocument="\xxx\AbstractEthernetPort",
* cascade="all",
* mappedBy="device"
* )
*/
protected $ports = array();
/** SETTER and GETTERS **/
}
These both classes are linked togehter with ReferenceOne and ReferenceMany. The code has beend slightly changed for this post.
This are the both versions of the testcase. The First does not work, the second does:
public function testPorts() {
$dm = self::$container->get('doctrine_mongodb')->getManager();
$sideASwitch = new Device();
$sideASwitch->setName("Switch01");
$copper1 = new Port();
$copper1->setDescription("Copper Port");
$copper2 = new Port();
$copper2->setDescription("Copper Port");
$sideASwitch->setPorts(array($copper1, $copper2));
$dm->persist($sideASwitch);
$dm->flush();
$x = $dm->getRepository("Device")->findOneBy(array());
\Doctrine\Common\Util\Debug::dump($x,1);
}
The query at the end returns an ports array with 0 content.
public function testPorts() {
$dm = self::$container->get('doctrine_mongodb')->getManager();
$sideASwitch = new Device();
$sideASwitch->setName("Switch01");
$copper1 = new Port();
$copper1->setDescription("Copper Port");
$copper2 = new Port();
$copper2->setDescription("Copper Port");
// ADDITIONAL
$copper1->setDevice($sideASwitch);
$copper2->setDevice($sideASwitch);
$sideASwitch->setPorts(array($copper1, $copper2));
$dm->persist($sideASwitch);
$dm->flush();
$x = $dm->getRepository("Device")->findOneBy(array());
\Doctrine\Common\Util\Debug::dump($x,1);
}
This Query returns an ports array with 2 objects in it...
Is this the normal behaviour in Doctrine ODM or are am i doing something wrong?
Thanks for any help
This is the expected behavior. Calling $sideASwitch->setPorts(array($copper1, $copper2)); has no effect because ports is the mapped side.
As a convenience, I often do something like the following on the mapped (Device) side:
public function setPorts(array $ports)
{
foreach($ports as $port) {
$port->setDevice($this);
}
return $this;
}

Testing symfony 2 forms

I develop new type, but I don't know how I can test it.
Assert annotation is not load and validations is not called.
Could any one please help me?
class BarcodeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->
add('price');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Bundles\MyBundle\Form\Model\Barcode',
'intention' => 'enable_barcode',
));
}
public function getName()
{
return 'enable_barcode';
}
}
A have following model for storing form data.
namepspace Bundles\MyBundle\Form\Model;
class Barcode
{
/**
* #Assert\Range(
* min = "100",
* max = "100000",
* minMessage = "...",
* maxMessage = "..."
* )
*/
public $price;
}
I develop some test like this, the form didn't get valid data but it is valid! (Because annotation is not applied)
I try adding ValidatorExtension but I dont know how can I set constructor paramaters
function test...()
{
$field = $this->factory->createNamed('name', 'barcode');
$field->bind(
array(
'price' => 'hello',
));
$data = $field->getData();
$this->assertTrue($field->isValid()); // Must not be valid
}
Not sure why you need to unit-test the form. Cant You unit test validation of Your entity and cover controller with your expected output?
While testing validation of entity You could use something like this:
public function testIncorrectValuesOfUsernameWhileCallingValidation()
{
$v = \Symfony\Component\Validator\ValidatorFactory::buildDefault();
$validator = $v->getValidator();
$not_valid = array(
'as', '1234567890_234567890_234567890_234567890_dadadwadwad231',
"tab\t", "newline\n",
"Iñtërnâtiônàlizætiøn hasn't happened to ", 'trśżź',
'semicolon;', 'quote"', 'tick\'', 'backtick`', 'percent%', 'plus+', 'space ', 'mich #l'
);
foreach ($not_valid as $key) {
$violations = $validator->validatePropertyValue("\Brillante\SampleBundle\Entity\User", "username", $key);
$this->assertGreaterThan(0, count($violations) ,"dissalow username to be ($key)");
}
}
Functional test. Given that you generate a CRUD with app/console doctrine:generate:crud with routing=/ss/barcode, and given that maxMessage="Too high" you can:
class BarcodeControllerTest extends WebTestCase
{
public function testValidator()
{
$client = static::createClient();
$crawler = $client->request('GET', '/ss/barcode/new');
$this->assertTrue(200 === $client->getResponse()->getStatusCode());
// Fill in the form and submit it
$form = $crawler->selectButton('Create')->form(array(
'ss_bundle_eavbundle_barcodetype[price]' => '12',
));
$client->submit($form);
$crawler = $client->followRedirect();
// Check data in the show view
$this->assertTrue($crawler->filter('td:contains("12")')->count() > 0);
// Edit the entity
$crawler = $client->click($crawler->selectLink('Edit')->link());
/* force validator response: */
$form = $crawler->selectButton('Edit')->form(array(
'ss_bundle_eavbundle_barcodetype[price]' => '1002',
));
$crawler = $client->submit($form);
// Check the element contains the maxMessage:
$this->assertTrue($crawler->filter('ul li:contains("Too high")')->count() > 0);
}
}
Include this line must be in Model and try it after include look like your model.
/* Include the required validators */
use Symfony\Component\Validator\Constraints as Assert;
namespace Bundles\MyBundle\Form\Model;
class Barcode
{
/**
* #Assert\Range(
* min = "100",
* max = "100000",
* minMessage = "min message here",
* maxMessage = "max message here"
* )
*/
public $price;
}

Idea discussion: dynamic view script switching in zend MVC implementation [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 11 years ago.
This is basically the "Am i doing it right?" question.
I have an idea how i can transparently switch views for default/mobile version/admin areas at run time. And I would like to know what pros and cons you see in this approach.
Main requirements are:
switch entire application with
little to no coding
integrate into zend MVC workflow,
not overwrite it
fallback to default
preserve standard functionality
controllers shouldn't be aware of
changes
Here is my pseudohelper
class Xrks_Controller_Action_Helper_VrExtension extends Zend_Controller_Action_Helper_Abstract
{
public function postDispatch()
{
if(!$this->_shouldRender()) {
return; //just skip
}
try {
$vr = $this->_getViewRenderer();
$backupView = clone $vr->view;
$this->_setBasePaths(); //set base path(s) through ViewRenderer::initView($path)
$oldSpecArray = $this->_setVrPathSpecs(); //set VR view script path specs
$vr->render();
$vr->setNoRender(true); //disable renderer
} catch(Zend_View_Exception $e) { //fallback to default viewscripts if view script file not found
$vr->setView($backupView); //restore view on error
} catch(Exception $e) {
$vr->setView($backupView); //restore view on error
$this->_setVrPathSpecs($oldSpecArray); //restore script path spec
throw $e;
}
$this->_setVrPathSpecs($oldSpecArray);//restore script path spec
}
/**
* Same functionality as ViewRenderer helper _shouldRender method
* #return boolean
*/
protected function _shouldRender();
/**
* #return Zend_Controller_Action_Helper_ViewRenderer
*/
protected function _getViewRenderer();
/**
* Sets viewRenderer path specifications
*
* #param array $spec if NULL uses $this->_viewRendererPathSpecs
* #return array old path spec (0 => pathSpec, 1 => pathNoControllerSpec)
*/
protected function _setVrPathSpecs(array $spec = NULL);
}
How exactly helper should be configured is not important and that part skipped
Here is example how it supposed to work:
$this->_setBasePaths(); sets view base paths to application/views/default/ and application/views/admin/
$this->_setVrPathSpecs(); set path specification to ':module/:controller/:action.:suffix'
so for foo-baz-bar it will search at
1. application/views/admin/scripts/foo/baz/bar.phtml
2. application/views/default/scripts/foo/baz/bar.phtml
if view script not found fall back to default ViewRenderer:
3. application/modules/foo/views/scripts/baz/bar.phtml
Ask questions if I missed something
Upd: After some research i decided to use action helper to autoregister view scriptPaths based on specification for inflector and specified variables. I also modified partial helpers to register scriptPaths if partial from other module requested.
This is crude but working version of action helper:
class Xrks_Controller_Action_Helper_ViewRendererPathstack extends Zend_Controller_Action_Helper_Abstract
{
const PATH_APPEND = 'append';
const PATH_PREPEND = 'prepend';
protected $_enabled = FALSE;
protected $_viewScriptPaths = array();
/**
* By default following vars available: baseDir, area, theme, module
* #var string
*/
protected $_viewScriptPathSpec = ':baseDir/:area/:module';
protected $_defaults = array(
'area' => 'frontend',
'theme' => 'default',
);
protected $_vars = array();
protected $_inflector;
protected $_viewRenderer;
public function __construct($baseDir = NULL)
{
if($baseDir == NULL) {
$baseDir = APPLICATION_PATH . DS . 'views';
}
$this->setDefaultVar('baseDir', $baseDir);
$this->addPath(array());
}
/**
* Enter description here ...
* #return Zend_Controller_Action_Helper_ViewRenderer
*/
protected function _getViewRenderer()
{
if(!$this->_viewRenderer) {
$this->_viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
}
return $this->_viewRenderer;
}
/**
* Should the ViewRenderer render a view script?
*
* #return boolean
*/
protected function _shouldRender()
{
$vR = $this->_getViewRenderer();
return (!$this->getFrontController()->getParam('noViewRenderer')
&& !$vR->getNeverRender()
&& !$vR->getNoRender()
&& (null !== $vR->getActionController())
&& $vR->getRequest()->isDispatched()
&& !$vR->getResponse()->isRedirect()
);
}
public function generatePaths(array $vars = array())
{
$this->_registerVarsWithInflector();
$vars = array_merge($this->_defaults, $this->_vars, $vars);
$inflector = $this->getInflector();
$generatedPaths = array();
foreach($this->_viewScriptPaths as $path) {
$pathVars = array_merge($vars, $path);
$generatedPaths[] = $inflector->filter($pathVars);
}
return array_reverse(array_unique(array_reverse($generatedPaths)));//last occurence more important than first
// array('test', 'test2', 'test') => array('test2', 'test')
// #todo rethink this code piece later. must be better solution
}
protected function _registerVarsWithInflector()
{
$vars = array_merge($this->_defaults, $this->_vars);
$inflector = $this->getInflector();
$unregistered = array_keys(array_diff_key($vars, $inflector->getRules()));
sort($unregistered, SORT_DESC);//more specific first (moduleDir prior to module key)
foreach($unregistered as $var) {
$inflector->addFilterRule($var, array('Word_CamelCaseToDash', 'StringToLower'));
}
}
protected function _viewAddScriptPaths(Zend_View_Abstract $view, $paths)
{
foreach ($paths as $path) {
$view->addScriptPath($path);
}
}
/**
* Get inflector
*
* #return Zend_Filter_Inflector
*/
public function getInflector()
{
if (null === $this->_inflector) {
$this->_inflector = new Zend_Filter_Inflector();
$this->_inflector->setThrowTargetExceptionsOn(true);
//setup default rules
$this->_inflector->addRules(array(
':baseDir' => array(),
))
->setTargetReference($this->_viewScriptPathSpec);
}
return $this->_inflector;
}
/**
*
* #return array
*/
public function getPaths()
{
return $this->_basePaths;
}
public function getEnabled()
{
return $this->_enabled;
}
public function setEnabled($flag = TRUE)
{
$this->_enabled = (bool)$flag;
return $this;
}
/**
*
* #todo add check for $pathVars keys and values validity
* #param array $pathVars associative array
* #param string $placement either append or prepend
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack
*/
public function addPath(array $pathVars, $placement = self::PATH_APPEND)
{
if($placement == self::PATH_PREPEND) {
array_unshift($this->_viewScriptPaths, $pathVars);
} else {
$this->_viewScriptPaths[] = $pathVars;
}
return $this;
}
/**
*
* #param array|Zend_Config $paths
* #param string $placement either append or prepend
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack
* #throws Xrks_Exception
*/
public function addPaths($paths, $placement = self::PATH_APPEND)
{
if($paths instanceof Zend_Config) {
$paths = $paths->toArray();
} elseif (!is_array($paths)) {
throw new Xrks_Exception('$paths should be either array or instance of Zend_Config');
}
if($placement == self::PATH_PREPEND) {
$paths = array_reverse($paths);
}
foreach($paths as $path) {
$this->addPath((array)$path, $placement);
}
return $this;
}
/**
*
* #param array $pathVars associative array
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack
*/
public function setPath(array $pathVars)
{
$this->_basePaths = array();
$this->addPath($pathVars);
return $this;
}
/**
*
* #param array|Zend_Config $paths
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack
* #throws Xrks_Exception
*/
public function setPaths($paths)
{
$this->_basePaths = array();
$this->addPaths($paths);
return $this;
}
/**
*
* #param string $varName
* #return string |NULL
*/
public function getDefaultVar($varName)
{
if(key_exists($varName, $this->_defaults)) {
return $this->_defaults[$varName];
}
return NULL;
}
/**
* #param string $varName
* #param string $value
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack Provides fluent interface
*/
public function setDefaultVar($varName, $value)
{
$this->_defaults[$varName] = (string)$value;
return $this;
}
/**
*
* #param string $name
* #return string |NULL
*/
public function getVar($name, $defaults = false)
{
if(key_exists($name, $this->_vars)) {
return $this->_vars[$name];
}
return $defaults ? $this->getDefaultVar($name) : NULL;
}
/**
* #param string $varName
* #param string $value
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack Provides fluent interface
*/
public function setVar($varName, $value)
{
$this->_vars[$varName] = $value;
return $this;
}
public function unsetVar($name)
{
if(key_exists($name, $this->_vars)) {
unset($this->_vars[$name]);
}
return $this;
}
public function postDispatch()
{
if(!$this->getEnabled() || !$this->_shouldRender()) {
return; //just skip
}
try {
$vr = $this->_getViewRenderer();
$this->setVar('module', $vr->getModule());
$paths = $this->generatePaths();
$this->_viewAddScriptPaths($vr->view, $paths);
if(Zend_Registry::isRegistered('Zend_Log')) {
Zend_Registry::get('Zend_Log')
->log($paths, Zend_Log::DEBUG);
}
} catch(Exception $e) {
if(Zend_Registry::isRegistered('Zend_Log')) {
Zend_Registry::get('Zend_Log')
->log($e, Zend_Log::WARN);
}
throw $e;
}
}
}
The way I usually handle this:
I register a Layout Plugin, extending Zend_Layout_Controller_Plugin_Layout
I use the preDispatch hook to determine what module, controller, action I am in
I switch between layouts and views depending on the context
For me, that's by far the easiest method.
GJ