Overriding Template in Shopware 4 from plugin - templates

public function install() {
$this->subscribeEvent(
'Enlight_Controller_Action_PostDispatchSecure_Frontend',
'onFrontendPostDispatch',
0
);
return array('success' => true, 'invalidateCache' => array('frontend'));
}
public function onFrontendPostDispatch(\Enlight_Event_EventArgs $args)
{
/** #var \Enlight_Controller_Action $controller */
$controller = $args->get('subject');
$view = $controller->View();
$view->addTemplateDir(
__DIR__ . '/Views'
);
}
I had tried to run the plugin and override Template but Shopware does not see changes in a plugin.
I am creating new file in /Views/frontend/checkout/cart_footer.tpl in plugins root.
I am also insert
{extends file='parent:frontend/checkout/cart_footer.tpl'}
line in .tpl file but still no success.
Does any one know where is a problem?

This was very easy
I just add one line
$view->loadTemplate('frontend/plugins/checkout/cart.tpl');
And change code little bit.
I am change event from Enlight_Controller_Action_PostDispatchSecure_Frontend to
Enlight_Controller_Action_PostDispatch_Frontend_Checkout
and add $view->loadTemplate('frontend/plugins/checkout/cart.tpl');
This path is related from "/Views" folder which is declared in addTemplateDir method.
Bellow is whole code, Enjoy :)
public function install() {
$this->subscribeEvent(
'Enlight_Controller_Action_PostDispatch_Frontend_Checkout',
'onFrontendPostDispatch'
);
return array('success' => true, 'invalidateCache' => array('frontend'));
}
public function onFrontendPostDispatch(\Enlight_Event_EventArgs $args)
{
/** #var \Enlight_Controller_Action $controller */
$controller = $args->get('subject');
$view = $controller->View();
$view->addTemplateDir(
__DIR__ . '/Views'
);
$view->loadTemplate('frontend/plugins/checkout/cart.tpl');
}

Related

How to add Fenom Template in Yii2?

I'm trying to add Fenom Tempate to Yii2 application, but I'm getting a lot of different kinds errors.
I've tried to create a component ViewRenderer and to write code like here, but with my own namespace:
namespace app\components\fenom;
use Yii;
use yii\base\View;
use yii\base\ViewRenderer as BaseViewRenderer;
class ViewRenderer extends BaseViewRenderer {
/**
* #var string the directory, where stores templates.
*/
public $templatePath = '#app/views';
/**
* #var string the directory, where stores compiled templates in PHP files.
*/
public $compilePath = '#runtime/Fenom/compile';
/**
* #var int|array bit-mask or array of Fenom settings.
* #see https://github.com/bzick/fenom/blob/master/docs/en/configuration.md#template-settings
*/
public $options = 0;
/**
* #var \Fenom object that renders templates.
*/
public $fenom;
public function init() {
$this->fenom = \yii\fenom\Fenom::factory($this->templatePath, $this->compilePath, $this->options);
}
public function render($view, $file, $params) {
$params['this'] = $view;
return $this->fenom->fetch($file, $params);
}
}
Added this component to config
'components' => [
'view' => [
'class' => 'yii\web\View',
'renderers' => [
'tpl' => [
'class' => 'app\components\fenom\ViewRenderer',
'options' => [
'auto_reload' => true,
],
],
// ...
],
],
But I'm getting errors. Bad namespace or unwritable directory or another and another errors.
So, my question is: How to add Fenom to Yii2? What and Where should I write (in config, components, or other folders)? What way is the fastest and the most efficient?
Please tell me how to do it properly?
Well, I did it. I'm not sure if this is correct, but...
I made the fenom folder inside the components folder. I put files from the src folder of fenom repo into the /components/fenom.
Also in this folder I created a ViewRenderer.php file. It contains code:
<?php
namespace app\components\fenom;
use Yii;
use yii\base\ViewRenderer as BaseViewRenderer;
class ViewRenderer extends BaseViewRenderer {
/**
* #var string the directory, where stores templates.
*/
public $templatePath = '#app/views';
/**
* #var string the directory, where stores compiled templates in PHP files.
*/
public $compilePath = '#runtime/Fenom/compile';
/**
* #var int|array bit-mask or array of Fenom settings.
* #see https://github.com/bzick/fenom/blob/master/docs/en/configuration.md#template-settings
*/
public $options = ['auto_reload' => true];
/**
* #var \Fenom object that renders templates.
*/
public $fenom;
public function init() {
// put main Fenom class into the yii classmap
Yii::$classMap['Fenom'] = __DIR__.'/Fenom.php';
// call Fenom class autoloader (https://github.com/fenom-template/fenom/blob/master/docs/en/start.md#custom-loader)
\Fenom::registerAutoload(__DIR__."./");
// Yii::getAlias - because it's not understand Yii aliases???
$this->fenom = \Fenom::factory(Yii::getAlias($this->templatePath), Yii::getAlias($this->compilePath), $this->options);
}
public function render($view, $file, $params) {
$params['this'] = $view;
$dirPath = '';
// this is because Fenom do not understand absolute paths???
if (strpos($file, 'views') != false)
$dirPath = explode('views', $file)[1];
if (strpos($file, 'widgets') != false)
$dirPath = explode('widgets', $file)[1];
if (strpos($file, 'modules') != false)
$dirPath = explode('modules', $file)[1];
return $this->fenom->fetch($dirPath, $params);
}
}
I've added ViewRenderer component into the config file:
'components' => [
'view' => [
'class' => 'yii\web\View',
'renderers' => [
'tpl' => [
'class' => 'app\components\fenom\ViewRenderer',
],
],
],
// ...
and created folders inside the runtime folder
- runtime
- Fenom
- compile
- cache
compile - for compiled filed, cache - for cached filed
That's it.
For testing:
/views/site/index.tpl file contains:
{$testtext}
/controllers/SiteController → actionIndex contains:
public function actionIndex() {
return $this->render('index.tpl', ['testtext' => 'It works! Test text']);
}
result:
Something like that...
Installation
Since fenom is an extension on its own, you should use composer to install it. From the docs:
The preferred way to install this extension is through composer.
Either run
php composer.phar require --prefer-dist y2i/yii2-fenom "*"
or add
"y2i/yii2-fenom": "*"
to the require section of your composer.json file.
Usage
The class element in your config file should be a fully qualified class name, not a path to the class file. As such, if installed using composer, you can use the following:
'class' => 'y2i\fenom\ViewRenderer'

Hook a twig template to a block in Drupal 8

I created a module which creates a custom block :
<?php
/**
* Provides a 'SLS Block' Block
*
* #Block(
* id = "SLS-Subheader",
* admin_label = #Translation("SLS Subheader"),
* )
*/
namespace Drupal\subheader\Plugin\Block;
use Drupal\Core\Block\BlockBase;
class SubheaderBlock extends BlockBase {
/**
* {#inheritdoc}
*/
public function build() {
return array(
'#title' => "test",
);
}
}
?>
The module name is "subheader"
In my subheader.module i want to hook a specific template:
<?php
/**
* Implements hook_theme().
*/
function subheader_theme() {
return array(
'slssubheader' => array(
'variables' => array('pierre' => NULL),
'template' => 'specifictemplate',
),
);
}
I tried all kind of naming convention for the function name and the array key, but always unsuccesful. It never hook the template to specifictemplate.html.twig
Anyone has an idea??
Thanks a LOOOOTTT
Pierre
I had the same problem, though probably a different cause. Google lead me to your question though. The issue with your code is the missing #theme key in your build method I believe:
public function build() {
return array(
'#title' => "test",
'#theme' => 'slssubheader' // this one
);
}
In my case I had to search for a couple of hours before I found out I accidentally added a custom namespace to my .module file. Drupal doesn't like that and didn't recognize any of my hooks.

Unit test Laravel middleware

I am trying to write unit tests for my middleware in Laravel. Does anyone know a tutorial, or have an example of this ?
I have been writing a lot of code, but there must be a better way to test the handle method.
Using Laravel 5.2, I am unit testing my middleware by passing it a request with input and a closure with assertions.
So I have a middleware class GetCommandFromSlack that parses the first word of the text field in my Post (the text from a Slack slash command) into a new field called command, then modifies the text field to not have that first word any more. It has one method with the following signature: public function handle(\Illuminate\Http\Request $request, Closure $next).
My Test case then looks like this:
use App\Http\Middleware\GetCommandFromSlack;
use Illuminate\Http\Request;
class CommandsFromSlackTest extends TestCase
{
public function testShouldKnowLiftCommand()
{
$request = new Illuminate\Http\Request();
$request->replace([
'text' => 'lift foo bar baz',
]);
$mw = new \App\Http\Middleware\GetCommandFromSlack;
$mw->handle($request,function($r) use ($after){
$this->assertEquals('lift', $r->input('command'));
$this->assertEquals('foo bar baz',$r->input('text'));
});
}
}
I hope that helps! I'll try to update this if I get more complicated middleware working.
To actually test the middleware class itself you can do:
public function testHandle()
{
$user = new User(['email'=>'...','name'=>'...']);
/**
* setting is_admin to 1 which means the is Admin middleware should
* let him pass, but oc depends on your handle() method
*/
$user->is_admin = 1;
$model = $this->app['config']['auth.model'];
/**
* assuming you use Eloquent for your User model
*/
$userProvider = new \Illuminate\Auth\EloquentUserProvider($this->app['hash'], $model);
$guard = new \Illuminate\Auth\Guard($userProvider, $this->app['session.store']);
$guard->setUser($user);
$request = new \Illuminate\Http\Request();
$middleware = new \YourApp\Http\Middleware\AuthenticateAdmin($guard);
$result = $middleware->handle($request, function(){ return 'can access';});
$this->assertEquals('can access',$result);
}
I thinking the best solution is just checking what happened after middleware. For example, the authentication middleware:
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Authenticate {
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
* #return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest())
{
if ($request->ajax())
{
return response('Unauthorized.', 401);
}
else
{
return redirect()->guest('auth/login');
}
}
return $next($request);
}
}
And my test unit:
<?php
class AuthenticationTest extends TestCase {
public function testIAmLoggedIn()
{
// Login as someone
$user = new User(['name' => 'Admin']);
$this->be($user);
// Call as AJAX request.
$this->client->setServerParameter('HTTP_X-Requested-With', 'XMLHttpRequest');
$this->call('get', '/authpage');
$this->assertEquals(200, $response->getStatusCode());
}
}
I would do it in that way.
I was working on a localization Middleware that sets the app locale based on a URI segment, e.g. http://example.com/ar/foo should set the app local to Arabic. I basically mocked the Request object and tested as normal. Here is my test class:
use Illuminate\Http\Request;
use App\Http\Middleware\Localize;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class LocalizeMiddlewareTest extends TestCase
{
protected $request;
protected $localize;
public function setUp()
{
parent::setUp();
config(['locale' => 'en']);
config(['app.supported_locales' => ['en', 'ar']]);
$this->request = Mockery::mock(Request::class);
$this->localize = new Localize;
}
/** #test */
public function it_sets_the_app_locale_from_the_current_uri()
{
$this->request->shouldReceive('segment')->once()->andReturn('ar');
$this->localize->handle($this->request, function () {});
$this->assertEquals('ar', app()->getLocale());
}
/** #test */
public function it_allows_designating_the_locale_uri_segment()
{
$this->request->shouldReceive('segment')->with(2)->once()->andReturn('ar');
$this->localize->handle($this->request, function () {}, 2);
$this->assertEquals('ar', app()->getLocale());
}
/** #test */
public function it_throws_an_exception_if_locale_is_unsupported()
{
$this->request->shouldReceive('segment')->once()->andReturn('it');
$this->request->shouldReceive('url')->once()->andReturn('http://example.com/it/foo');
$this->setExpectedException(
Exception::class,
"Locale `it` in URL `http://example.com/it/foo` is not supported."
);
$this->localize->handle($this->request, function () {});
}
}
And here is my Middleware class:
namespace App\Http\Middleware;
use Closure;
class Localize
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param integer $localeUriSegment
* #return mixed
*/
public function handle($request, Closure $next, $localeUriSegment = 1)
{
$locale = $request->segment($localeUriSegment);
if (in_array($locale, config('app.supported_locales')))
{
app()->setLocale($locale);
}
else
{
abort(500, "Locale `{$locale}` in URL `".$request->url().'` is not supported.');
}
return $next($request);
}
}
Hope that helps :)

CakePHP 2.4 mock a method in a model

I want to test a model and for one of those tests I want to mock a method of the model I am testing. So I don't test a controller and I don't want to replace a whole model, just one method of the same model I test.
Reason is that this model method calls a file upload handler. This feature is already tested elsewhere.
What I am doing now is:
I test the model 'Content'. There I test it's method 'addTeaser', which calls 'sendTeaser'.
SO I want to mock sendTeaser and fake a successful answer of the method sendTeaser, while testing addTeaser.
That looks like this:
$model = $this->getMock('Content', array('sendTeaser'));
$model->expects($this->any())
->method('sendTeaser')
->will($this->returnValue(array('ver' => ROOT.DS.APP_DIR.DS.'webroot/img/teaser/5/555_ver.jpg')));
$data = array(
'Content' => array(
'objnbr' => '555',
'name' => '',
...
)
)
);
$result = $model->addTeaser($data);
$expected = true;
$this->assertEquals($expected, $result);
When I let my test run, I get an error that a model within the method 'sendTeaser' is not called properly. Hey! It shouldn't be called! I mocked the method!
..... or not?
What would be the proper syntax for mocking the method?
Thanks a lot as always for help!
Calamity Jane
Edit:
Here is the relevant code for my model:
App::uses('AppModel', 'Model');
/**
* Content Model
*
* #property Category $Category
*/
class Content extends AppModel {
public $dateipfad = '';
public $fileName = '';
public $errormessage = '';
public $types = array(
'sqr' => 'square - more or less squarish',
'hor' => 'horizontal - clearly wider than high',
'lnd' => 'landscape - low but very wide',
'ver' => 'column - clearly higher than wide',
);
public $order = "Content.id DESC";
public $actsAs = array('Containable');
public $validateFile = array(
'size' => 307200,
'type' => array('jpeg', 'jpg'),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
public $hasMany = array(
'CategoriesContent' => array(
'className' => 'CategoriesContent',
),
'ContentsTag' => array(
'className' => 'ContentsTag',
),
'Description' => array(
'className' => 'Description',
)
);
/**
* Saves the teaser images of all formats.
*
* #param array $data
*
* #return Ambigous <Ambigous, string, boolean>
*/
public function addTeaser($data)
{
$objnbr = $data['Content']['objnbr'];
$type = $data['Content']['teaser-type'];
if (!empty($data['Content']['teaser-img']['tmp_name'])) {
$mFileNames = $this->sendTeaser($data, $objnbr, $type);
}
if (!is_array($mFileNames)) {
$error = $mFileNames;
//Something failed. Remove the image uploaded if any.
$this->deleteMovedFile(WWW_ROOT.IMAGES_URL.$mFileNames);
return $error;
}
return true;
}
/**
* Define imagename and save the file under this name.
*
* Since we use Imagechache, we don't create a small version anymore.
*
* #param integer $objnbr
* #param string $teasername
*
* #return multitype:Ambigous <string, boolean> |Ambigous <boolean, string>
*/
public function sendTeaser($data, $objnbr, $type)
{
//$path = str_replace('htdocs','tmp',$_SERVER['DOCUMENT_ROOT']);
$this->fileName = $this->getImageName($objnbr, $type);
$oUH = $this->getUploadHandler($data['Content']['teaser-img']);
debug($oUH);
exit;
$error = $oUH->handleFileUpload();
if (empty($type))
$type = 0;
if ($error === 'none'){
// Send to ImageChacheServer
$oICC = $this->getImagecacheConnector();
$sCacheUrl = $oICC->uploadFile($objnbr, $type, $this->fileName);
debug($sCacheUrl);
return array($type => $this->fileName);
}
return $error;
}
public function getUploadHandler($imgdata)
{
App::uses('UploadHandler', 'Lib');
$oUH = new UploadHandler($this, $imgdata);
return $oUH;
}
}
Changing getMock to getMockForModel didn't change the output though.
I'd like to emphasize the answer from #ndm using Cake test helper class CakeTestCase::getMockForModel()
$theModel = CakeTestCase::getMockForModel('Modelname', ['theMethodToMock']);
$theModel->expects($this->once())
->method('theMethodToMock')
->will($this->returnValue('valueToReturn'));
$this->getMock is not the way to mock. You should use $this->generate
I would reccomend you to read a book about CakePHP unti testing, like this: https://leanpub.com/cakephpunittesting

Silex and Doctrine ORM

I am trying to use Silex together with Doctrine ORM (not just DBAL) but I am unable to get the configuration correct.
composer.json
{
"require": {
"silex/silex": "1.0.*#dev",
"symfony/monolog-bridge": "~2.1",
"symfony/twig-bridge": "~2.1",
"symfony/form": "~2.1",
"symfony/yaml": "2.2.*",
"symfony/form": "2.2.*",
"symfony/translation": "~2.1",
"symfony/config": "2.2.*",
"dflydev/doctrine-orm-service-provider": "1.0.*#dev"
},
"autoload": {
"psr-0": {
"Entities": "src/"
}
}
}
bootstrap.php located in my project root folder
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
require_once __DIR__ ."/vendor/autoload.php";
$isDevMode = true;
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src/Entities"), $isDevMode);
$params = array(
'driver' => 'pdo_sqlite',
'path' => __DIR__ . '/development.sqlite',
);
$entityManager = EntityManager::create($params, $config);
cli-config.php also located inside the root folder
require_once "bootstrap.php";
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($entityManager->getConnection()),
'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager)
));
Customer.php entity located inside src/Entities
/**
* #Entity #Table(name="customers")
**/
class Customer {
/** #Id #Column(type="integer") #GeneratedValue **/
protected $id;
/** #Column(type="string") **/
protected $name;
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function getId() {
return $this->id;
}
}
I am able to run commands like php vendor/bin/doctrine orm:schema-tool:create and have it generate a table called customs just as it should. But how do I load that entity inside my Silex application
Here is my index.php
require_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
use Symfony\Component\Yaml\Yaml;
$app['config'] = function () {
$config = Yaml::parse(__DIR__ .'/../config.yml');
return $config;
};
$app->register(new Silex\Provider\DoctrineServiceProvider(), array(
'dns.options' => $app['config']['database']['development']
));
$app->register(new Dflydev\Silex\Provider\DoctrineOrm\DoctrineOrmServiceProvider, array(
'orm.em.options' => array(
'mappings' => array(
array(
'type' => 'annotation',
'path' => __DIR__ .'/src/Entities',
)
)
),
));
$app->get('/', function () use ($app) {
$customer = $app['orm.em']->getRepository('Customer');
return '<pre>'. $customer->getName() .'</pre>';
});
The result when loading the localhost inside my browser
Warning: class_parents() [function.class-parents]: Class Customer does not exist and could not be loaded in /Users/me/Documents/project/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php on line 40
UPDATE
I am not sure this is the correct way to solve this issue, but by using the following approach the problem got solved and I can now use my entities in Silex
$app['em'] = function ($app) {
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src/Entities"), true);
$params = array(
'driver' => 'pdo_sqlite',
'path' => __DIR__ . '/../development.sqlite',
);
$entityManager = EntityManager::create($params, $config);
return $entityManager;
};
I used the dependency approach because that way I can use $app['config'] to store DB information and other environment specific configurations.
$customer = new \Entities\Customer();
$customer->setName('Multi Corp '. uniqid());
$app['em']->persist($customer);
$app['em']->flush();
I presume your doctrine Entity mappings reside under "/src/Entities" in the namespace \Entities. With your autoloader directive they should be accessible as \Entities\MyMappingCls.
Your problem seems to be that you don't give the fq-name of the mapping class when getting the repository. You need to give a string that can be resolved by the autoloader. Please try:
$app['orm.em']->getRepository('Entities\Customer');
You can also try to run orm:generate-proxies as they are only generated on the fly in debug mode (not so sure this is relevant).
hth