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');
Related
I am modularizing laravel. I have decided to move all the default routes, controllers, resources, etc.. to /app/Modules/Pub. For the most part this has worked well. However I would like to change the default resources path of the application. Unfortunately this doesn't seem to be (easily) configurable.
So... using grep I was able to track down the resource_path() function to /var/www/sigma/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
I think it's possible to override this function somewhere but this seems like a subpar hack as this function consists simply of:
app()->resourcePath($path)
Again using grep I found out that this function is to be found in /var/www/sigma/vendor/laravel/framework/src/Illuminate/Foundation/Application.php
This seems to be the thing to change since it does not reference any configuration value, rather the value is hard coded:
return $this->basePath.DIRECTORY_SEPARATOR.'resources'.($path ? DIRECTORY_SEPARATOR.$path : $path);
But I think it's safe to assume it's pretty foolish to change anything under the vendor folder manually. Obviously I need to override this function somewhere. I am unclear where and how to do this
Create a new Application class which extends the \Illuminate\Foundation\Application:
<?php
namespace <YOUR NAMESPACE HERE>;
class ApplicationCustom extends \Illuminate\Foundation\Application
{
public function __construct()
{
parent::__construct();
}
/**
* Get the path to the resources directory.
*
* #param string $path
* #return string
*/
public function resourcePath($path = '')
{
// Implement the custom method
}
}
Now, just change your bootstrap/app.php file to use the custom class:
$app = new YOUR_NAMESPACE\ApplicationCustom(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
Hope it helps.
You could create a class somewhere in your project and extend the default \Illuminate\Foundation\Application class. Then override the methods you need and switch the class instantiated in bootstrap/app.php with your custom one.
I'm using Laravel 5.5. The nature of the website is a 'multisite' architecture where multiple websites/domains are run from the same codebase.
I've come across an issue when sending email. I need to change the from name and address as well as the transport (SMTP, etc) options depending on which website is being viewed. I have these details stored in a config file.
The easiest way is to just pull those details in the Controller before I call Mail::send/Mail::queue and to update them. However, this brings back 2 issues:
There is a heavy reliance on remembering to actually do that every time I send any email in the code. In short, it's not abiding by DRY.
I'd be forced to use Mail::send instead of Mail::queue, because the queue wouldn't have any idea of the config update from the time it was queued only from when it is processed .
How can I achieve what I am looking to do here in a clean way?
I thought about extending all of my 'Mailable' classes with a custom class that updates the SMTP details, but it doesn't look like you can update the SMTP/Transport information after the class is initiated; you can only update the from name and address.
I managed to find a way to do this.
I had my mailable class (ContactFormMailable) extend a custom class, as follows:
<?php
namespace CustomGlobal\Mail;
use CustomGlobal\Mail\CustomMailable;
use CustomGlobal\ContactForm;
class ContactFormMailable extends CustomMailable
{
public $contact_form;
/**
* Create a new message instance.
*
* #return void
*/
public function __construct(ContactForm $contact_form)
{
$this->contact_form = $contact_form;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
$view = $this->get_custom_mail_view('contact_form', $this->contact_form);
return $this->subject('Contact Form Enquiry')
->view($view);
}
}
You'll notice I'm calling get_custom_mail_view. This is in my extended class and used to calculate the view and template I need to use for my mail, depending on the website being viewed. In here I also set the location of my config folder.
<?php
namespace CustomGlobal\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
use Swift_Mailer;
use Swift_SmtpTransport;
use CustomGlobal\Website;
use CustomGlobal\Territory;
class CustomMailable extends Mailable
{
use Queueable, SerializesModels;
public $layout_view_to_serve;
public $host_folder;
/**
* Override Mailable functionality to support per-user mail settings
*
* #param \Illuminate\Contracts\Mail\Mailer $mailer
* #return void
*/
public function send(Mailer $mailer)
{
app()->call([$this, 'build']);
$config = config($this->host_folder .'.mail');
// Set SMTP details for this host
$host = $config['host'];
$port = $config['port'];
$encryption = $config['encryption'];
$transport = new Swift_SmtpTransport( $host, $port, $encryption );
$transport->setUsername($config['username']);
$transport->setPassword($config['password']);
$mailer->setSwiftMailer(new Swift_Mailer($transport));
$mailer->send($this->buildView(), $this->buildViewData(), function ($message) use($config) {
$message->from([$config['from']['address'] => $config['from']['name']]);
$this->buildFrom($message)
->buildRecipients($message)
->buildSubject($message)
->buildAttachments($message)
->runCallbacks($message);
});
}
/**
* Calculate the template we need to serve.
* $entity can be any object but it must contain a
* $website_id and $territory_id, as that is used
* to calculate the path.
*/
public function get_custom_mail_view($view_filename, $entity)
{
if(empty($view_filename)) {
throw new Exception('The get_custom_mail_view method requires a view to be passed as parameter 1.');
}
if(empty($entity->website_id) || empty($entity->territory_id)) {
throw new Exception('The get_custom_mail_view method must be passed an object containing a website_id and territory_id value.');
}
// Get the website and territory
$website = Website::findOrFail($entity->website_id);
$territory = Territory::findOrFail($entity->territory_id);
$view_to_serve = false;
$layout_view_to_serve = false;
// Be sure to replace . with _, as Laravel doesn't play nice with dots in folder names
$host_folder = str_replace('.', '_', $website->website_domain);
$this->host_folder = $host_folder; // Used for mail config later
/***
Truncated for readability. What's in this area isn't really important to this answer.
***/
$this->layout_view_to_serve = $layout_view_to_serve;
return $view_to_serve;
}
}
It's important to remember that mail can be queued. If you do this is another way, such as setting a config at runtime, then you'll find that the process that runs the queue has no visibility/scope of your runtime config changes, and you'll end up firing out email from your default values.
I found a few answers similar to this one, which helped me out, but none of them worked completely, and some are out-dated (Swift_SmtpTransport is changed considerably since those answers).
Hopefully this helps someone else out.
I wanted to create a custom basepath helper to replace the original zf2 basepath view helper.
So if i call $this->basepath, it will use my custom basepath instead of the original one. I am not sure if this is can be done. I want my custom basepath extends the original basepath class too.
I have found some answers on how to create custom helpers and how to register them in module.php or module.config.php
But i can't find any similar questions on how to override the original helpers!
Factory definition of the basepath view helper is declared as a hardcoded invokable in HelperPluginManager (on line 45) however this definition also overridden in ViewHelperManagerFactory (line 80 to 93) because BasePath view helper requires the Request instance from ServiceLocator:
$plugins->setFactory('basepath', function () use ($serviceLocator) {
// ...
})
I strongly recommend extending the built-in basepath helper with a different name (MyBasePath for example) instead of trying to override the existing one. Overriding that native helper may produce some unexpected headaches later (think about 3rd party modules which uses that helper to work).
For your question; yes, it is possible.
Create the Application\View\Helper\BasePath.php helper class like below:
namespace Application\View\Helper;
use Zend\View\Helper\BasePath as BaseBasePath; // This is not a typo
/**
* Custom basepath helper
*/
class BasePath extends BaseBasePath
{
/**
* Returns site's base path, or file with base path prepended.
*/
public function __invoke($file = null)
{
var_dump('This is custom helper');
}
}
And override the factory in the onBootstrap() method of the Module.php file like below:
namespace Application;
use Zend\Mvc\MvcEvent;
use Application\View\Helper\BasePath; // Your basepath helper.
use Zend\View\HelperPluginManager;
class Module
{
/**
* On bootstrap for application module.
*
* #param MvcEvent $event
* #return void
*/
public function onBootstrap(MvcEvent $event)
{
$services = $event->getApplication()->getServiceManager();
// The magic happens here
$services->get('ViewHelperManager')->setFactory('basepath', function (HelperPluginManager $manager) {
$helper = new BasePath();
// Here you can do whatever you want with the instance before returning
return $helper;
});
}
}
Now you can try in any view like this:
echo $this->basePath('Bar');
This is not a perfect solution but it should work.
I'm responsible for a rather large web app I built 8 years ago, then later refactored using ZF1 and now trying to move beyond that into more modern framework components. At the moment am trying to see if I can swap out Zend_View for Phalcon\Mvc\View\Simple without having to touch every .phtml file.
Problem I've run into is that while both assign a variable to the view in the same way (e.g. $this->view->foo = 'bar'), in Zend_View in the template you would <?=$this->foo;?> to print the var but in Phalcon it is <?=$foo;?>.
As I mentioned I don't want to go through each of several hundred .phtml files to remove $this->. Is there a way I can override Phalcon's render() or otherwise enable access to the view params using $this?
Here's what I came up with after fiddling with it all day. Simply extend the PHP view engine:
class Engine extends \Phalcon\Mvc\View\Engine\Php
{
/**
* Renders a view using the template engine
*
* #param string $path
* #param array $params
* #param boolean $mustClean
* #return string
*/
public function render($path, $params = null, $mustClean = null)
{
/**
* extract view params into current object scope
* so we can access them with <?=$this->foo;?>
* Maintains backward compat with all the .phtml templates written for Zend_View
*/
foreach($this->_view->getParamsToView() as $key => $val) {
$this->$key = $val;
}
return parent::render($path, $params, $mustClean);
}
You can use DI container to access any registered services in the view, so just put your variables into DI (in the action for example):
public function indexAction()
{
$this->getDi()->set('hello', function() { return 'world'; });
...
And then use it in the template via $this variable:
<div>
<?php echo $this->hello; ?>
</div>
P.S. This is not a good way to assign variables to the view, but should help in your particular case.
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?