Symfony: Adding messages to the translator dynamically (updated)

This is an update to the previous translator post, allowing the system to use the default translator and just adding the required messages to the message-catalogue in-use.

This is a better approach as it doesn’t then mean you miss out on the good things Symfony provides out-of-the-box because you’ve had to define what specific component is in use for the translator.

Anyway — code is below;

services.yml
No changes are needed — no translator specifics are needed (apart from including a listener as in the previous post)

MyListener.php

Add a function to your kernel.event-listener as below;

/**
 * @param TranslatorInterface $translator
 */
public function addTranslatorResources(TranslatorInterface $translator) {
   $locale = $translator->getLocale();
   $catalogue = $translator->getCatalogue($locale);
   
   $translations = ['messages' => ['key1' => 'my message is here']];
   $loader = new ArrayLoader();
   $customCatalogue = $loader->load($translations, $locale);
      
   $catalogue->addCatalogue($customCatalogue);
}

Test using the following;

// debug code to test;
 echo $translator->trans('messages.key1');
References;

https://symfony.com/doc/current/translation.html

Symfony: Adding to the translator dynamically

Symfony – Validator component with Dependency Injection

If you’re using the validation/validator component (eg. https://symfony.com/doc/current/components/validator.html) outside of the symfony framework and need to access a service within a custom constraint, here’s how to do it;

Create a ContainerConstraintValidatorFactory;

If you’re using the validation/validator component outside of the Symfony framework and need to access a service within a custom constraint (eg. http://symfony.com/doc/current/validation/custom_constraint.html), here’s how to do it;

Create a ContainerConstraintValidatorFactory;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidatorFactory;
use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;

class ContainerConstraintValidatorFactory extends ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface {
 private $container;
 
 public function __construct(ContainerInterface $container) {
  parent::__construct();
  $this->container = $container;
 }
 
 public function getInstance(Constraint $constraint) {
  if($this->container->has($constraint->validatedBy())) {
   return $this->container->get($constraint->validatedBy());
  }
 
  return parent::getInstance($constraint);
 }
}

From there, when you’re creating your ‘validator’, you’ll need to use your newly created factory;

$validatorBuilder = Validation::createValidatorBuilder();
$validatorBuilder->setConstraintValidatorFactory(
   new ContainerConstraintValidatorFactory($container)
);
$validator = $validatorBuilder->getValidator();

This will use your container to check if the constraint-validator class is registered … if so, it’ll be used, otherwise it’ll behave as normal.

Reference: https://stackoverflow.com/questions/40601866/how-to-configure-dependencies-on-custom-validator-with-symfony-components

Symfony: Adding to the translator dynamically

UPDATE: Symfony: Adding messages to the translator dynamically (updated) contains an update to the post below

To add to the translator (eg. from the DB / by user / etc), you need to first create your own translator class or force Symfony to use the base one (otherwise it’ll compile things together and use a read-only one when it comes to accessing your site).

First, in your services.yml file;

translator:
    public: true
    alias: 'Symfony\Bundle\FrameworkBundle\Translation\Translator'

Symfony\Bundle\FrameworkBundle\Translation\Translator:
  class: Symfony\Bundle\FrameworkBundle\Translation\Translator
  arguments:
      - '@service_container'
      - '@translator.selector'
      - '%kernel.default_locale%'

This just tells Symfony to use the base translator, or alternatively you can use your own as follows;

app/config/services.yml

translator:
  public: true
  alias: 'YourNamespace\YourTranslatorClassName'

From there you can add a listener or just need to obtain an instance of the Translator on-load, and add in any resources as required – eg;

your-listener / controller / etc;

use Symfony\Component\Translation\Loader\ArrayLoader

...

$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', ["my.word" => "Hello"], $translator->getLocale());

$translator is an instance of “Symfony\Component\Translation\Translator”.

More info;
https://symfony.com/doc/current/components/translation/usage.html

Symfony – Generating absolute urls

Generating absolute urls (including hostname and scheme (http / https) is super useful for including full urls in templates or email content (eg. when you want to pass on an address to someone which is getting your emails!)

How?

In templates;

{{ absolute_url(path('route_name', {...})) }}

In controllers;

use Symfony\Component\Routing\Generator\UrlGeneratorInterface;


$url = $this->generate(
'app_default_index',
['name' => 'Steve'], UrlGeneratorInterface::ABSOLUTE_URL
);

In services;

First import the class name for the url generator, and instance of the generator;

use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/** @var UrlGeneratorInterface */
protected $urlGenerator;

/**
 * Inject URL Generator as a service
 * @param UrlGeneratorInterface $urlGenerator
 * @required
 */
public function setUrlGeneratorService(UrlGeneratorInterface $urlGenerator) {
    $this->urlGenerator = $urlGenerator;
}

Then, inside your method (in the service);

$url = $this->urlGenerator->generate(
'app_default_index',
['name' => 'Steve'], UrlGeneratorInterface::ABSOLUTE_URL
);

Debugging Webpack

This has caused me several days of exciting times… some of the errors Webpack gives can be a little misleading, showing one error when there’s in-fact a totally different underlying issue which will fix everything!

The following shows you everything you need;

<./node_modules/.bin/encore (or your command here> --watch --progress --display-error-details --verbose

You’ll need to replace the start of this command with something relevant to you, depending on the location of your encore / webpack executable.

Webpack Encore & CometD

This drove me nuts for hours;

These dependencies were not found:
* org/cometd in ./node_modules/cometd-jquery/jquery/jquery.cometd.js

The solution … in your webpack.config.js file;

config = Encore.getWebpackConfig();
config.resolve.alias["org/cometd"] = path.resolve(__dirname, "./node_modules/cometd-jquery/org/cometd");
config.resolve.alias["jquery.cometd"] = path.resolve(__dirname, "./node_modules/cometd-jquery/jquery/jquery.cometd");

// export the final configuration
module.exports = config;

After that you should be all good to go!

If you get an error about the ‘path’ function not existing, then just include the following at the top of the file;

var path = require('path');

SSL Keys for JWT

https://help.ubuntu.com/community/SSH/OpenSSH/Keys
http://unix.stackexchange.com/questions/26924/how-do-i-convert-a-ssh-keygen-public-key-into-a-format-that-openssl-pem-read-bio

Generate 4096 bit keys;

ssh-keygen -t rsa -b 4096

Convert to PEM format;

openssl rsa -in ms-test -pubout -outform pem > ms-test.pub-509

WordPress – updates for private/premium plugins

Add a hook to ‘site_transient_update_plugins’ (filter) to inject an update to get update data for your plugin.

This lets you add data about update information which wordpress has retrieved from it’s own server about any free plugins you’ve installed.

Query your site with the latest version info, which will in-turn be added to the update data to be used with wordpress with the rest of the plugin update data.

Use get_transient($transient_name) to get cached data (eg. version info) which is cached for a period of time (set_transient) so your server doesn’t get hit every time a person hits the site. (look for the force-cache variable in the $_GET vars when the ‘Check again…’ button is used.

Also use the plugins_api to show plugin data when the plugin update-details is shown.

From talk by Elliot Condon (elliotcondon.com)