How to add a custom view helper in zend framework2? - model-view-controller

I used the skeleton zf2 application from github

here is an excerpt from the ZF2 reference that should get you started:
14.4.2. Registering Helpers Zend\View\Renderer\PhpRenderer composes a plugin broker for managing helpers, specifically an instance of
Zend\View\HelperBroker, which extends the base plugin broker in order
to ensure we have valid helpers available. The HelperBroker by default
uses Zend\View\HelperLoader as its helper locator. The HelperLoader is
a map-based loader, which means that you will simply map the
helper/plugin name by which you wish to refer to it to the actual
class name of the helper/plugin.
Programmatically, this is done as follows:
// $view is an instance of PhpRenderer
$broker = $view->getBroker();
$loader = $broker->getClassLoader();
// Register singly:
$loader->registerPlugin('lowercase', 'My\Helper\LowerCase');
// Register several:
$loader->registerPlugins(array(
'lowercase' => 'My\Helper\LowerCase',
'uppercase' => 'My\Helper\UpperCase',
));
Within an MVC application, you will typically simply pass a map of
plugins to the class via your configuration.
// From within a configuration file
return array(
'di' => array('instance' => array(
'Zend\View\HelperLoader' => array('parameters' => array(
'map' => array(
'lowercase' => 'My\Helper\LowerCase',
'uppercase' => 'My\Helper\UpperCase',
),
)),
)),
);
The above can be done in each module that needs to register helpers
with the PhpRenderer; however, be aware that another module can
register helpers with the same name, so order of modules can impact
which helper class will actually be registered!
14.4.3. Writing Custom Helpers Writing custom helpers is easy. We recommend extending Zend\View\Helper\AbstractHelper, but at the
minimum, you need only implement the Zend\View\Helper interface:
namespace Zend\View;
interface Helper
{
/**
* Set the View object
*
* #param Renderer $view
* #return Helper
*/
public function setView(Renderer $view);
/**
* Get the View object
*
* #return Renderer
*/
public function getView();
}
If you want your helper to be capable of being invoked as if it were a
method call of the PhpRenderer, you should also implement an
__invoke() method within your helper.
As previously noted, we recommend extending
Zend\View\Helper\AbstractHelper, as it implements the methods defined
in Helper, giving you a headstart in your development.
Once you have defined your helper class, make sure you can autoload
it, and then register it with the plugin broker.
Here is an example helper, which we're titling "SpecialPurpose"
namespace My\View\Helper;
use Zend\View\Helper\AbstractHelper;
class SpecialPurpose extends AbstractHelper
{
protected $count = 0;
public function __invoke()
{
$this->count++;
$output = sprintf("I have seen 'The Jerk' %d time(s).", $this->count);
return htmlspecialchars($output, ENT_QUOTES, 'UTF-8');
}
}
Then assume that when we register it with the plugin broker, we map it
to the string "specialpurpose".
Within a view script, you can call the SpecialPurpose helper as many
times as you like; it will be instantiated once, and then it persists
for the life of that PhpRenderer instance.
// remember, in a view script, $this refers to the Zend_View instance.
echo $this->specialPurpose();
echo $this->specialPurpose();
echo $this->specialPurpose();
The output would look something like this:
I have seen 'The Jerk' 1 time(s). I have seen 'The Jerk' 2 time(s). I
have seen 'The Jerk' 3 time(s).

Related

Laravel 5.7: prevent auto load injection in controller constructor

I have a HomeController with his constructor that takes a Guzzle instance.
/**
* Create a new controller instance.
*
* #param \GuzzleHttp\Client|null $client
*
* #return void
*/
public function __construct(Client $client = null)
{
$this->middleware('auth');
$this->middleware('user.settings');
if ($client === null) {
$param = [
'base_uri' => 'http://httpbin.org/',
'defaults' => [
'exceptions' => false,
'verify' => false
]
];
$client = new Client($param);
}
$this->setClient($client);
}
I would use via __constructor() to be able to mock it in tests.
My issues is that Laravel automatically auto-load the injection and the Guzzle Client injected has blank defaults (and cannot anymore edit it). In other words: at first call of HomeController Client is not null. And I need as null.
How can I stop this behaviour (only for the __construct() for HomeController)? I really use the DI in every part of my webapp.
EDIT
I just find that if I don't type-hints the Client, of course Laravel cannot auto-load. Is this the right mode to work?
New constructor:
public function __construct($client = null)
Thank you
I had a simular situation when testing apis. I ended up binding an instance of GuzzleClient to the service container (see documentation). Something like:
$this->app->instance('GuzzleHttp\Client', new MockClient);
To successfully mock the instance, I then checked to see whether or not it had a certain property value (in my case base_url being set). That determined whether or not the instance was a test as base_url would be set.
Along side this method, GuzzleHttp\Client does have a MockHandler you may want to explore. This can be used to fake response bodies, headers and status codes.

Laravel - Facade explanation and code example

whenever we call a Facade Method it involves Facade design pattern and it called for some hidden class by using Facade. for instance for File, if we call
File::get(public_path().'test.txt');
this will call the method in class
Illuminate\Filesystem\Filesystem
and in this class we will have get($path) method.
Now my question is how Facade Abstract Class is related to File and Filesystem and where Laravel is telling them to call get in Filesystem. is there some kind of register which i am missing ?? i want to find complete link.
If you go in your config/app.php, you will notice that there's an array called aliases which looks like this
'aliases' => [
//
//
//
//
'File' => Illuminate\Support\Facades\File::class,
];
So, basically whenever you call File, the Service Container will try to resolve an instance of Illuminate\Support\Facades\File::class which is just a Facade.
If you look into Illuminate\Support\Facades\File::class, you will see that it contains only one method:
class File extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'files';
}
}
As you can see, it extends the Facade class and whenever a Facade is being resolved, Laravel will try to find a key in the Service Container that is equal to whatever is returned by getFacadeAccessor().
If you check the source of Illuminate\Filesystem\FilesystemServiceProvider, you will see this:
$this->app->singleton('files', function () {
return new Filesystem;
});
As you can see, the key files is being bounded to a FileSystem implementation. So, that's how Laravel knows how to resolve the File facade.

Laravel - How do I inject automatically in Validation Rule Construct?

I'm not able to inject these variables through Laravel:
//...class AllowedUsername implements Rule...
public function __construct(Router $router, Filesystem $files, Repository $config)
{
$this->router = $router;
$this->files = $files;
$this->config = $config;
}
I get the error:
Type error: Too few arguments to function ... 0 passed in.
Why is Laravel not doing it automatically?
$request->validate([
'username' => ['required', new AllowedUsername],
]);
In order to leverage Laravel's injection magic you need to use Laravel's API which essentially is:
resolve($class) which is wrapper around app($class)
app($class, $params = []) which is wrapper around:
Note: I've changed $abstract for $class
if (is_null($class)) {
return Container::getInstance();
}
return Container::getInstance()->make($class, $parameters);
Classes that you want to resolve out of container (as seen in your code sample):
public function __construct(Router $router, Filesystem $files, Repository $config)
can be resolved only because Laravel maintainers already defined binding for Router::class, Filesystem:class (example: FilesystemServiceProvider).
Repository::class seems to be simple class that does not require parameters (or require parameters that container already knows how to resolve) while "newing up" - thus Laravel can resolve it without problem.
There is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects, since it can automatically resolve these objects using reflection.
Thats why resolve(AllowedUser::class) or resolve(Router::class)... work.
In order to let Laravel know what constructor's parameters should be sent during "newing up" you use bindings mentioned in documentation.

Translate controller class variables in zend framework 2

Let's say I have a controller and I want to define some const variables that hold some messages (eg error messages etc).
Is there a way to make it so they are translated?
An example class is defined bellow:
<?php
namespace Test\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class AccountController extends AbstractActionController
{
protected $testError = 'There was an error while testing some stuff';
public function testAction(){
// I know i can use the following approach but I don't want to
// since I want to use a variable for readability issues.
// $testError = $this->getTranslator()->translate('There was an error..');
return new ViewModel();
}
/**
* Retrieve the translator
*
* #return \Zend\I18n\Translator\Translator
*/
public function getTranslator()
{
if (!$this->translator) {
$this->setTranslator($this->getServiceLocator()->get('translator'));
}
return $this->translator;
}
/**
* Set the translator
*
* #param $translator
*/
public function setTranslator($translator)
{
$this->translator = $translator;
}
}
So I want to have the testError translated. I know I can just use the message and translate it via the zend translator without using a variable, but still I want to store it in a variable for readability issues. Any help or other approaches to this?
Simply create a translations.phtml file in any directory in your project root and fill it something like that:
<?php
// Colors
_('Black');
_('White');
_('Green');
_('Light Green');
_('Blue');
_('Orange');
_('Red');
_('Pink');
In poedit, check Catalog Properties > Source keywords list an be sure _ character is exists. (Alias of the gettext method). In application, use $this->translate($colorName) for example.
When poedit scanning your project directory to find the keywords which needs to be translated, translations.phtml file will be scanned too.
Another handy approach is using _ method (gettext alias) to improve code readability. Example:
$this->errorMsg = _('There was an error..');
But don't forget to set the global Locale object's default locale value too when you initialising your translator instance first time in a TranslatorServiceFactory or onBootstrap method of the module:
...
$translator = \Zend\Mvc\I18n\Translator\Translator::factory($config['translator']);
$locale = 'en_US';
$translator->setLocale($locale);
\Locale::setDefault($translator->getLocale());
return $translator;
...
I don't quite understand what you mean:
$errorMessage = 'FooBarBazBat";
return new ViewModel(array(
'error' => $this->getTranslator()->translate($errorMessage)
));
would be a way to store the message inside a variable. But i really don't understand where your problem is.
Or do you mean having the translator as variable?
$translator = $this->getServiceLocator()->get('viewhelpermanager')->get('translate');
$errorMessage = $translator('FooBarBazBat');

Codeigniter: How can I check if a library method exists without loading the library?

I want to separate certain functions from controller methods into libraries to make them modular. Based on the URI I'm trying to route to a library method if it exists. But I'd have to load the library to check for the method with the php method_exists function. The only other way I've found to check for it is to put all the library methods in a config array and check for the method name there. Such as:
<?php
/**
* Application_config.php
*/
$config['extensions'] = array(
'News' => array(
'library' => 'articles_library',
'methods' => array(
'articles',
'article',
'edit_article',
'add_article',
'delete_article'
)
)
);
and
<?php
/**
* admin_controller.php
*
* all admin routes go to index
*/
class C3_base_controller extends Controller {
public function index() {
$lib_chk = 0;
$ext = $this->config->item('extensions');
foreach($ext as $item) {
foreach ($item['methods'] as $meth) {
if ($this->uri->segment(2) == $meth) {
$lib = $item['library'];
echo $this->$lib->$meth();
$lib_chk = 1;
}
}
}
if ($lib_chk == 0) {
// rest of controller...
}
}
}
Is there a better way to check for the existence of a library method in each library without loading the libraries? Having to duplicate every method in the application config is just asking for it.
You can use
method_exists
For example, if you are checking for the database object if it's exists
if(method_exists($this->CI->db, 'set')){
// code ...
}
Are you pursuing modularity for reusable code or are you creating an application that has features that may or may not be present...as in plugins?
It's hard for me to imagine your base application having hard-coaded url's that lead to a library that may not exist. Can you test for the library rather than the method? That may be simpler to check a directory and load file names into an array, then see if the library required by a url or uri is in that array.
But if you need to stick to your array of existing method names, could you just regex for the method names by that occur after "function (space)" and followed by parenthesis?

Resources