How to override system.mail.yml in Drupal 8? - smtp

I have the below code in my file: core\modules\system\config\install\system.mail.yml
interface:
default: 'php_mail'
I want to change the code to:
interface:
default: 'SMTPMailSystem'
In order to get my SMTP module to work. On changing the code in the core file my module works. Since making direct changes in core file is not good I want to know how do we override such files. I am fairly new to Drupal 8 hence couldn't get through.

Drupal has an article on Configuration override system, which gives an overview and starter code to override configurations defined in *.yml . You can jump to "Providing overrides from modules" section right away for your case.
In short:
Create a module (config_example used as an example)
Create a config_example.services.yml, and put:
services:
config_example.overrider:
class: \Drupal\config_example\ConfigExampleOverrides
tags:
- {name: config.factory.override, priority: 5}
config.factory.override is the important thing here, others are up to you to change.
Define the class which implements ConfigFactoryOverrideInterface:
namespace Drupal\config_example;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryOverrideInterface;
use Drupal\Core\Config\StorageInterface;
/**
* Example configuration override.
*/
class ConfigExampleOverrides implements ConfigFactoryOverrideInterface {
/**
* {#inheritdoc}
*/
public function loadOverrides($names) {
$overrides = array();
if (in_array('system.mail', $names)) { // (a)
$overrides['system.mail'] = [
'interface' => ['default' => 'SMTPMailSystem']
];
}
return $overrides;
}
/**
* {#inheritdoc}
*/
public function getCacheSuffix() {
return 'ConfigExampleOverrider'; // (c)
}
/**
* {#inheritdoc}
*/
public function getCacheableMetadata($name) {
return new CacheableMetadata();
}
/**
* {#inheritdoc}
*/
public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
return NULL;
}
}
The following thing is changed to work for your case:
(a) The in_array needle is changed to system.mail which is the YML you wish to override. The value assigned to $overrides['system.mail'] is changed to what you wish to be placed.

Related

Symfony, How to use DenyAccess with Doctrine object?

I want to control the same user access on some methods in my controller.
Currently, I use this :
$this->denyAccessUnlessGranted('ACCESS', $this->Player($toolRepository));
However I am forced to use this line and inject the ToolRepository into each method ...
What would be the easiest way to do it?
I saw that there was the IsGranted annotation but my subject needs to be a doctrine object to control access with my Vote.
/**
* #Route("/player")
*/
class PlayerController extends AbstractController
{
/**
* #Route("/", name="player")
* #throws Exception
*/
public function Player(ToolRepository $toolRepository): \App\Entity\Tool
{
$playerTool = 'TestTool2';
$tool = $toolRepository->findOneBy(array('libelle' => $playerTool));
if (!$tool) {
throw new Exception('Tool : ' . $playerTool . 'not found!');
}
return $tool;
}
/**
* #Route("/main", name="player")
* #IsGranted ("ACCESS", subject="tool")
* #throws Exception
*/
public function mainPlayer(PlayerRepository $playerRepository, ToolRepository $toolRepository): Response
{
$this->denyAccessUnlessGranted('ACCESS', $this->Player($toolRepository));
$players = $playerRepository->findAll();
return $this->render('player/player_mainpage.html.twig', ['players'=>$players]);
}
}
I think this ressource should answer you: Symfony voters.
You'll put your security logic in your custom voter which will be called in every function of your controller (or every methods where you want to control access) isGranted() function.
Calling your Player() function is a easier way to do this for beginner, you can keep like that but you shouldn't put it in Controller and use a Service instead .
Edit:
You may store your ToolRepository as Controller private property and inject it in a __construct() method so you don't have to inject ToolRepository in each method

Cakephp 3.2.6 admin prefix - Error: The application is trying to load a file from the Admin plugin

I am struggling from the migration of my 2.0 cakephp site to 3.2.6. I followed the cookboo on how to implement an admin section of my site. When link to my admin section I receive: Error: The application is trying to load a file from the Admin plugin. Below are my codes:
\config\routes.php
Router::prefix('admin', function ($routes) {
// All routes here will be prefixed with `/admin`
// And have the prefix => admin route element added.
$routes->fallbacks('DashedRoute');
});
\src\Controller\Admin
namespace App\Controller\Admin;
use App\Controller\AppController;
/**
* Quads Controller
*
* #property \App\Model\Table\QuadsTable $Quads
*/
class QuadsController extends AppController
{
/**
* Index method
*
* #return \Cake\Network\Response|null
*/
public function index()
{
$this->paginate = [
'contain' => ['Users', 'Categories', 'Genres', 'Venues']
];
$quads = $this->paginate($this->Quads);
$this->set(compact('quads'));
$this->set('_serialize', ['quads']);
}
All of my .ctp files are in \Template\Admin.
Cakephp 3 now requires themes to be in the plugin folder. The culprit code in my AppController.php was:
if ((isset($this->request->prefix) && ($this->request->prefix == 'admin')))
{
$this->theme = 'admin';
}

Typehint inherited class variables in PHPStorm

In PHPStorm, I can type-hint a variable this way:
/** #var Point $point */
$point->x();
However, say I inherited a variable from a parent class, and want to type-hint it:
class PointProxy extends Proxy
{
public function x()
{
...
/** #var Point $this->geometry */
return $this->geometry->x();
}
}
This doesn't work, PHPStorm acts as if I had type-hinted $this, and not $this->geometry.
Is there a way to make such a type-hint work without redeclaring the $geometry property in the subclass, or is this unsupported?
Try this code. Also you can press alt+enter at undefined properties and select Add #property it will help you to create phpdoc faster.
/**
* #property Point $geometry
*/
class PointProxy extends Proxy {
public function x() {
return $this->geometry->
}
}
If the parent object types the property as Geometry but you want it typed as a Point (which descends from Geometry) in your child class, I would recommend creating an accessor in your child class. Possibly with some type checking.
class PointProxy extends Proxy
{
/**
* Access the geometry object on parent class as a Point
*
* #return Point
*/
private point()
{
if(!is_a($this->geometry, 'Point'))
{
// Log an error or something, this is not a state we should be in
}
else
{
return $this->geometry;
}
}
public function x()
{
...
return $this->point->x();
}
}
I ran into a very similar problem. I had a generic storage class that dealt with database operations and then a proxy class on top which proxied all storage class methods through a try catch (using __call()) so that I could handle exceptions in one location.
Now whenever I accessed the storage instance like $storage->retrievePhoto($id), the PHPStorm IDE could not typehint for me. My solution involved adding another class name annotation to the $storage object.
For example, see below. Since the specific proxy class is really just a wrapper over the original storage class, it doesn't present any problems although it is still not 100% to my liking but it works.
final class PhotoRepository
{
/**
* #var \Repositories\Photos\PhotoStorage
* \Repositories\Photos\PhotoStorageExceptionHandlerProxy
*/
private $storage;
/**
* #param \Repositories\Photos\PhotosStorageExceptionHandlerProxy $storage
*/
public function __construct(PhotosStorageExceptionHandlerProxy $storage)
{
$this->storage = $storage;
}

PhpStorm cannot autocomplete model attributes

simply I want PhpStorm autocomplete my model's attributes when I use find(), findAll(), findByAttributes() etc...
I have a model like:
/**
* member model parameters:
* #property integer $id
* #property integer $city_id
* #property string $e_mail
*/
class Member extends CActiveRecord
{
/**
* #static
* #param string $className
* #return Member
*/
public static function model($className = __CLASS__)
{
return parent::model($className);
}
...
When I use active record methods like:
$member = Member::model()->findByAttributes(array('e_mail'=>'Foo Bar'));
and try to autocomplete when I wrote this:
$member->
It only gives me CActiveRecord's parameters and methods in the list.
I tried to change
/**
* Finds a single active record that has the specified attribute values.
* See {#link find()} for detailed explanation about $condition and $params.
* #param array $attributes list of attribute values (indexed by attribute names) that the active records should match.
* An attribute value can be an array which will be used to generate an IN condition.
* #param mixed $condition query condition or criteria.
* #param array $params parameters to be bound to an SQL statement.
* #return CActiveRecord the record found. Null if none is found.
*/
public function findByAttributes($attributes,$condition='',$params=array())
{...
this method's return param from CActiveRecord to Member, self, parent, $this, child etc...
Autocomplete only worked when it was "Member". But this method is used for all models not just the Member model so this is not a solution.
If anyone knows the solution (preferably without changing the framework core methods) I will be glad.
NOTE: All of my awesome Yii code is freely available on bitbucket in the repositories here and here. If you hate Yii's verbosity, check out my Pii class. I think you'll totally dig it.
I've run into this and what I do is augment the phpdoc for the static model() method. This corrects the issue and the autocomplete works fine.
For instance:
class MyModel extends CActiveRecord {
/**
* #static
* #param string $className
* #return MyModel|CActiveRecord
*/
public static function model($className=__CLASS__) {
.... yada yada yada ...
}
}
Note the pipe and additional class added to the "#return". This tells PhpStorm to also include that class in the auto-complete lookups.
One additional note, if you're using namespaces, you may need a slash in front of some class names. Just depends on your project and includes.
=============== UPDATE: 2013-08-05 ===============
With PhpStorm v6 and up, it looks like you can use:
*
* #return $this
*
And also get proper auto-completion.
On a side note, the whole static "model()" method thing is antiquated (like Yii) and I have a base model class that I use now for all projects. It contains the static model method; which is then no longer required in each of my subclasses. Here's an example...
<?php
namespace My\Awesome\Name\Space;
/**
* MyBaseModel
* etc.etc.
*/
class MyBaseModel extends \CActiveRecord
{
/**
* Returns the static model of the specified AR class.
*
* #param string $className
*
* #return $this
*/
public static function model( $className = null )
{
return parent::model( $className ? : \get_called_class() );
}
//code code code code
}
/**
* MySubModel
*/
class MySubModel extends MyBaseModel
{
/**
* {#InheritDoc}
*/
public function tableName()
{
return 'my_sub_table';
}
}
$_models = MySubModel::model()->findAll( 'xyz = :xyz', array( ':xyz' => 'abc' ) );
if ( !empty( $_models ) )
{
foreach ( $_models as $_model )
{
// Do awesome stuff...
}
}
Autocomplete works fine for all the subclasses...
Just thought I'd update this and let y'all know.
You can use phpdoc #method. You can use this approach for frequently-used models or you can create new template for code generator.
/**
* #method Member findByPk($pk,$condition='',$params=array())
*/
class Member extends CActiveRecord
{

Is there a way to specify Doctrine2 Entitymanager implementation class in Symfony2?

I'm currently working with Symfony2 and Doctrine2, but I must override the Doctrine2 EntityManager and add it some "undelete" features (ACLs inside).
So I'm wondering : is there a way to override the EntityManager class and specify Doctrine2 in Symfony2 to use it as implementation of the EntityManager?
Thank you for any help!
After Doctrine 2.4 (Doctrine 2.4 release) you need to use decorator for this. Do not extend EntityManager directly.
First you need to implement you own entity manager decorator that extends Doctrine\ORM\Decorator\EntityManagerDecorator (like #Dana)
But you can't just change doctrine.orm.entity_manager.class to your new decorator because EntityManagerDecorator requires EntityManagerInterface in it's constructor:
public function __construct(EntityManagerInterface $wrapped)
You can't just pass doctrine.orm.entity_manager as a parameter here because it will be a recursion.
And don't do like this:
return new self(\Doctrine\ORM\EntityManager::create(
What you need is to configure your decorator in services like a decorator:
yourcompany_entity_manager:
public: false
class: YourCompany\ORM\EntityManagerDecorator
decorates: doctrine.orm.default_entity_manager
arguments: ["#yourcompany_entity_manager.inner"]
Now you'll have your decorator as a default entity manager for Doctrine. #yourcompany_entity_manager.inner is actually a link to doctrine.orm.default_entity_manager that will be passed to yourcompany_entity_manager constructor.
Symfony docs for configuring decorators: link
Btw this command is very useful to debug your services:
app/console container:debug | grep entity_manager
Yes, it's possible with two steps:
1 - Override the doctrine.orm.entity_manager.class parameter to point to your custom entity manager (which should extend Doctrine\ORM\EntityManager.)
2 - Your custom entity manager must override the create method so that it returns an instance of your class. See my example below, and note the last line regarding MyEntityManager:
public static function create($conn, Configuration $config, EventManager $eventManager = null) {
if (!$config->getMetadataDriverImpl()) {
throw ORMException::missingMappingDriverImpl();
}
if (is_array($conn)) {
$conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, ($eventManager ? : new EventManager()));
} else if ($conn instanceof Connection) {
if ($eventManager !== null && $conn->getEventManager() !== $eventManager) {
throw ORMException::mismatchedEventManager();
}
} else {
throw new \InvalidArgumentException("Invalid argument: " . $conn);
}
// This is where you return an instance of your custom class!
return new MyEntityManager($conn, $config, $conn->getEventManager());
}
You'll also need to use the following in your class:
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\ORMException;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Connection;
To be honest, I'm surprised that the 2nd step is required at all, I would think this should be possible to accomplish using only the service container.
At least in Doctrine/ORM 2.4, the doc string of the EntityManager class explicitly discourages inheriting from Doctrine\ORM\EntityManager, instead they suggest inheriting from Doctrine\ORM\Decorator\EntityManagerDecorator:
/**
* The EntityManager is the central access point to ORM functionality.
* ...
* You should never attempt to inherit from the EntityManager: Inheritance
* is not a valid extension point for the EntityManager. Instead you
* should take a look at the {#see \Doctrine\ORM\Decorator\EntityManagerDecorator}
* and wrap your entity manager in a decorator.
* ...
*/
/* final */class EntityManager implements EntityManagerInterface
{
...
So, extend EntityManagerDecorator and make whatever changes you need. You will need to implement the create() factory method, but you don't need to copy EntityManager's implementation now:
use Doctrine\ORM\Decorator\EntityManagerDecorator;
use Doctrine\Common\EventManager;
use Doctrine\ORM\Configuration;
class MyEntityManager extends EntityManagerDecorator
{
/**
* {#inheritDoc}
*/
public function persist($entity)
{
// do something interesting
parent::persist($entity);
}
public function create($conn, Configuration $config, EventManager $eventManager = null)
{
return new self(\Doctrine\ORM\EntityManager::create($conn, $config, $eventManager));
}
}
Then override the doctrine.orm.entity_manager.class parameter to point to your custom entity manager class.
The docs don't cover everything, in many cases you just have to read the code.
I found the process of extending the entity manager extremely counterintuitive,
despite a decent grasp of concepts including dependency injection, service locator, code generation, caching and the decorator pattern.
Hopefully this concise example will paint a clear picture for you (this expands on the answer by #user2563451)
Symfony Version (Mon Aug 20 13:05:58 CEST 2018)
$ composer info | grep -E -e symfony/framework -e 'doctrine/(common|orm|dbal)'
doctrine/common v2.9.0 Common Library for Doctrine projects
doctrine/dbal v2.8.0 Database Abstraction Layer
doctrine/orm v2.6.2 Object-Relational-Mapper for PHP
symfony/framework-bundle v4.1.3 Symfony FrameworkBundle
config/services.yaml
App\Doctrine\ORM\CustomEntityManager:
public: false # optional afaik
decorates: doctrine.orm.original_entity_manager
arguments: [ '#App\Doctrine\ORM\CustomEntityManager.inner' ]
config/packages/doctrine.yaml
doctrine:
orm:
auto_generate_proxy_classes: '%kernel.debug%'
default_entity_manager: original
entity_managers:
original:
connection: from_env
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: false
mappings:
TimeTracking:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/php/Model'
prefix: TimeTracking\Model
alias: TimeTracking
mapping: true
#mapper_number_5:
# (...)
src/php/App/Doctrine/ORM/CustomEntityManager.php
<?php
namespace App\Doctrine\ORM;
use App\Doctrine\ORM\Proxy\SoggyProxyFactory;
use Doctrine\ORM\Decorator\EntityManagerDecorator;
use Doctrine\ORM\Proxy\ProxyFactory;
/**
* Writes custom proxy-class methods with support for the set-or-get-trait
* #property ProxyFactory soggyProxyFactory
*/
class CustomEntityManager extends EntityManagerDecorator
{
/// SUPER: __construct(EntityManagerInterface $wrapped) { $this->wrapped = $wrapped; }
private $soggyProxyFactory;
public function getProxyFactory() {
$config = $this->getConfiguration();
if (null === $this->soggyProxyFactory) {
$this->soggyProxyFactory = new SoggyProxyFactory(
$this,
$config->getProxyDir(),
$config->getProxyNamespace(),
$config->getAutoGenerateProxyClasses()
);
}
return $this->soggyProxyFactory;
}
}
references
http://symfony.com/doc/current/service_container/service_decoration.html
https://symfony.com/doc/current/doctrine/multiple_entity_managers.html