How to override SyliusCoreBundle Model User - symfony

I try to add a new field "phone" in model User (SyliusCoreBundle/Model/User).
Avoiding to touch SyliusCoreBundle,
I create a new bundle 'ShopBundle' which is beside of the others sylius bundles to override base user class :
src/Sylius/Bundle/ShopBundle
in the folder ShopBundle :
> /Controller(empty)
> /DependencyInjection(empty)
> /Model
> /User.php
> /Resources
> /config/doctrine/model/user.orm.xml
> /config/service.xml (empty)
> SyliusShopBundle.php
In file src/Sylius/Bundle/ShopBundle/Model/User.php, I have :
<?php
namespace Sylius\Bundle\ShopBundle\Model;
use Sylius\Bundle\CoreBundle\Model\User as BaseUser;
class User extends BaseUser
{
protected $mobile;
/**
* {#inheritdoc}
*/
public function setMobile($mobile)
{
$this->mobile = $mobile;
}
/**
* {#inheritdoc}
*/
public function getMobile()
{
return $this->mobile;
}
}
In file src/Sylius/Bundle/ShopBundle/Resources/config/doctrine/model/user.orm.xml, I have :
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<mapped-superclass name="Sylius\Bundle\ShopBundle\Model\User" table="sylius_user">
<field name="mobile" column="mobile" type="string" nullable="true" />
</mapped-superclass>
</doctrine-mapping>
In file src/Sylius/Bundle/ShopBundle/SyliusShopBundle.php, I have :
class SyliusShopBundle extends Bundle
{
/**
* Return array with currently supported drivers.
*
* #return array
*/
public static function getSupportedDrivers()
{
return array(
SyliusResourceBundle::DRIVER_DOCTRINE_ORM
);
}
}
I add this line in app/AppKernel.php
new Sylius\Bundle\ShopBundle\SyliusShopBundle(),
Final, I do commend like :
php app/console doctrine:schema:update --dump-sql
I got nothing to update in database.
Which part i missed ? What can i do to make it works ? Thanks !!
I added two files in folder DependencyInjection
Configuration.php
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('sylius_shop');
$rootNode
->addDefaultsIfNotSet()
->children()
->scalarNode('driver')->cannotBeOverwritten()->isRequired()->cannotBeEmpty()->end()
->end()
;
$this->addClassesSection($rootNode);
return $treeBuilder;
}
/**
* Adds `classes` section.
*
* #param ArrayNodeDefinition $node
*/
private function addClassesSection(ArrayNodeDefinition $node)
{
$node
->children()
->arrayNode('classes')
->addDefaultsIfNotSet()
->children()
->arrayNode('user')
->addDefaultsIfNotSet()
->children()
->scalarNode('model')->defaultValue('Sylius\\Bundle\\ShopBundle\\Model\\User')->end()
->end()
->end()
->end()
->end()
->end()
;
}
}
SyliusShopExtension.php
<?php
namespace Sylius\Bundle\ShopBundle\DependencyInjection;
use Sylius\Bundle\ResourceBundle\DependencyInjection\SyliusResourceExtension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class SyliusShopExtension extends SyliusResourceExtension
{
/**
* #var array
*/
private $bundles = array();
/**
* {#inheritdoc}
*/
public function load(array $config, ContainerBuilder $container)
{
$this->configDir = __DIR__.'/../Resources/config';
$this->configure($config, new Configuration(), $container, self::CONFIGURE_LOADER | self::CONFIGURE_DATABASE | self::CONFIGURE_PARAMETERS);
}
}

It should be User.orm.xml, not user.orm.xml.
You have to configure the class under sylius_core -> classes -> user -> model node.
You should not inspire your bundle by Sylius bundles, and definitely not put in under "Sylius" namespace. Just create a very basic Symfony bundle and put your User entity under Entity namespace, Symfony won't see it under Model.

I would add that in order to use Sylius fixtures you have to configure the user resource too:
sylius_resource:
resources:
sylius.user:
classes:
model: MyBundle\UserBundle\Entity\User

Related

Do Subscribers work while loading Fixtures in Symfony?

I tried to run the fixture below on Symfony 5 using the command php bin/console d:f:l.
I get this error: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'contact_email' cannot be null
The same code logic is working fine for Post entities when creating them manually through the CRUD. Are fixtures not compatible with subscribers (events) or did i make a mistake?
Thank you.
Edit: I'm also using EasyAdmin Bundle 3.
App\DataFixtures.php\AppFixtures.php
<?php
namespace App\DataFixtures;
use App\Entity\User;
use App\Entity\Author;
use Doctrine\Persistence\ObjectManager;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
class AppFixtures extends Fixture
{
/** #var User[] */
private $users = [];
/** #var Author[] */
private $authors = [];
/** #var UserPasswordHasherInterface */
private $hasher;
public function __construct(UserPasswordHasherInterface $hasher)
{
$this->hasher = $hasher;
}
public function load(ObjectManager $manager): void
{
$this->createUsers();
foreach($this->users as $user) $manager->persist($user);
$this->createAuthors();
foreach($this->authors as $author) $manager->persist($author);
$manager->flush();
}
public function createUsers(): void
{
$admin = (new User)
->setUsername('admin')
->setEmail('admin#admin.com')
->setRoles(['ROLE_ADMIN'])
->setFirstname('Edouard')
->setLastname('Proust');
$admin->setPassword($this->hasher->hashPassword($admin, 'admin'));
$this->users[] = $admin;
}
public function createAuthors(): void
{
foreach($this->users as $user) {
if(in_array('ROLE_ADMIN', $user->getRoles())) {
$author = (new Author)
->setUser($user)
->setAvatar('#')
->setBio('Bio')
// The line i want to get rid of:
// ->setContactEmail($user->getEmail())
;
$this->authors[] = $author;
}
}
}
}
App\EventListener\AuthorSubscriber.php
<?php
namespace App\EventListener;
use App\Entity\Author;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityPersistedEvent;
class AuthorSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
BeforeEntityPersistedEvent::class => 'setContactEmail',
];
}
public function setContactEmail(BeforeEntityPersistedEvent $event)
{
/** #var Author */
$entity = $event->getEntityInstance();
if($entity instanceof Author) {
if(!$entity->getContactEmail()) {
$user = $entity->getUser();
$contactEmail = $user ? $user->getEmail() : '#';
$entity->setContactEmail($contactEmail);
}
}
}
}
EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityPersistedEvent:class is not proper Symfony event name. You probably should use Doctrine\ORM\Events::prePersist.
Also please check your DoctrineBundle version. If you're using the default services.yaml configuration and DoctrineBundle lower than 2.1, you have to configure services.yaml with:
App\EventListener\AuthorSubscriber:
tags:
- name: 'doctrine.event_subscriber'
You can read something more here: https://symfony.com/doc/current/doctrine/events.html#doctrine-lifecycle-subscribers

symfony 4 configuration default value ( no file )

I'm trying to create a default configuration for my bundle. I created the bundle, bundle extension and configuration class. Inside the configuration class, I define a couple of keys with a default value. No yaml files are created. When I try to dump the configuration, I got the error:
"The extension with alias "foxtrot_alpha_users" does not have configuration."
But if I create the matching yaml file, and define at least one of the keys, the other key takes the default value as stated in the configuration class the value can be overridden.
Is it possible to define a default configuration with no yaml file at all?
Bundle class:
class UsersBundle extends Bundle
{
/**
* this is to have a custom alias
*
* #return void
*/
public function getContainerExtension()
{
return( new UsersExtension() );
}
}
Extension class:
class UsersExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
foreach($config as $key => $value)
{
$parameterKey = $this->getAlias().'.'.$key;
$container->setParameter($parameterKey, $value);
}
$container->setParameter('default_password', $config['default_password']);
}
/**
* customized alias
*
* #return string
*/
public function getAlias()
{
return('foxtrot_alpha_users');
}
Configuration class:
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('foxtrot_alpha_users');
$rootNode = $treeBuilder->getRootNode();
$this->addUserParameters( $rootNode );
return $treeBuilder;
}
private function addUserParameters( ArrayNodeDefinition $rootNode )
{
$rootNode
->addDefaultsIfNotSet()
->children()
->scalarNode('default_password')
->defaultValue('248')
->end()
->scalarNode('x')
->defaultValue('1')
->end()
->end()
;
return( $rootNode );
}

Symfony - Get custom param in bundle controller

I'm running a website under Symfony 3.4.12 and I created my own custom bundle. I have a custom config file in Yaml :
# src/CompanyBundle//Resources/config/config.yml
company_bundle:
phone_number
... and it is launched this way :
<?php
# src/CompanyBundle/DependencyInjection/CompanyExtension.php
namespace CompanyBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class CompanyExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('config.yml');
}
}
?>
I would like to retrieve my custom parameters in my controller file, what is the best way to do it ? I tried this way, with no success :
$this->getParameter('company_bundle.phone_number')
Thanks.
You have to define your own DependencyInjection/Configuration.php: http://symfony.com/doc/3.4/bundles/configuration.html#processing-the-configs-array
Like that:
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('company_bundle');
$rootNode
->children()
->scalarNode('phone_number')
->end()
->end()
;
}
And then process it into your DependencyInjection/...Extension.php file. If you want to make this option as parameter you have to do it like that:
public function load(array $configs, ContainerBuilder $container)
{
// Some default code
$container->setParameter('company_bundle.phone_number', $config['phone_number']);
}
And then you can get this parameter in your controller like you do.

Create entity relationships between bundles of differents providers

I'm developing my first Symfony2 Bundle "distributable" by packagist.org and have a few questions regarding how to relate the "user entity" from: "the actual project", to: "this external bundle".
What I do:
I created a GIT repository specifically for my bundle.
I have created and configured the composer.json file, as explained in packagist.org
Set the user entity from the general config.yml /app/config/config.yml
to send as parameter to the bundle .
// KDRMKLabs\TicketBundle\DependencyInjection\Configuration.php
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('kdr_ticket');
$rootNode->children()
->scalarNode('user_class')->isRequired()->cannotBeEmpty()->end()
->end()
;
return $treeBuilder;
}
}
# app\config\config.yml
kdr_ticket:
user_class: AppBundle\Entity\User
// KDRMKLabs\TicketBundle\DependencyInjection\KDRTicketExtension.php
class KDRTicketExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.xml');
$container->setParameter('kdr_ticket.model.user.class', $config['user_class']);
}
}
4. Ticket Entity
/**
* Ticket
*
* #ORM\Table(name="kdr_ticket")
* #ORM\Entity
*/
class Ticket
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* How I can tell to Doctrine that this object is the specified in config.yml ?
*/
private $user;
...
}
Using the User Entity when you install the bundle or when the database is updated for the first time after installation of the bundle
How Can I use the "user entity" specified in config.yml to create relations of the form "ManyToOne" when you update the database with the command doctrine:schema:update --force
I appreciate any help.
Thank you.

Service is non-existant? Symfony 2

I'm trying to get this simple notification service working and I am having no joy at all. I've never used services in symfony before so I could be overlooking something pretty basic, however it all seems correct to me so I'm kind of banging my head against a wall here.
I've included everything to do with the service, help would be really appreciated!
Stack Trace:
[1] Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "game.notify".
at n/a
in D:\web\www\mygame\app\bootstrap.php.cache line 1968
at Symfony\Component\DependencyInjection\Container->get('game.notify')
in D:\web\www\mygame\vendor\symfony\symfony\src\Symfony\Bundle\FrameworkBundle\Controller\Controller.php line 252
at Symfony\Bundle\FrameworkBundle\Controller\Controller->get('game.notify')
in D:\web\www\mygame\src\Game\MainBundle\Controller\PageController.php line 10
at Game\MainBundle\Controller\PageController->indexAction()
in line
at call_user_func_array(array(object(PageController), 'indexAction'), array())
in D:\web\www\mygame\app\bootstrap.php.cache line 2843
at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), '1')
in D:\web\www\mygame\app\bootstrap.php.cache line 2817
at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), '1', true)
in D:\web\www\mygame\app\bootstrap.php.cache line 2946
at Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel->handle(object(Request), '1', true)
in D:\web\www\mygame\app\bootstrap.php.cache line 2248
at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
in D:\web\www\mygame\web\app_dev.php line 28
Notify Controller:
Located at: Game/MainBundle/Controller/NotifyController.php
<?php
namespace Game\MainBundle\Controller;
class NotifyController
{
private $defaults
= array(
"type" => "flash",
),
$flashes = array();
/**
* #param \Symfony\Component\HttpFoundation\Session\Session $session
*/
public function __construct(\Symfony\Component\HttpFoundation\Session\Session $session)
{
$this->session = $session;
}
/**
* Depending on the supplied type argument, add the values
* to the session flashBag or $this->flashes
*
* #param string $name
* #param array $arguments
*/
public function add($name, array $arguments = array())
{
$arguments += $this->defaults;
// If the type is flash then add the values to the session flashBag
if ($arguments["type"] === "flash") {
$this->session->getFlashBag()->add($name, $arguments);
}
// Otherwise if its instant then add them to the class variable $flashes
elseif ($arguments["type"] === "instant") {
// We want to be able to have multiple notifications of the same name i.e "success"
// so we need to add each new set of arguments into an array not overwrite the last
// "success" value set
if (!isset($this->flashes[$name])) {
$this->flashes[$name] = array();
}
$this->flashes[$name][] = $arguments;
}
}
/**
* Check the flashBag and $this->flashes for existence of $name
*
* #param $name
*
* #return bool
*/
public function has($name)
{
if($this->session->getFlashBag()->has($name)){
return true;
} else {
return isset($this->flashes[$name]);
}
}
/**
* Search for a specific notification and return matches from flashBag and $this->flashes
*
* #param $name
*
* #return array
*/
public function get($name)
{
if($this->session->getFlashBag()->has($name) && isset($this->flashes[$name])){
return array_merge_recursive($this->session->getFlashBag()->get($name), $this->flashes[$name]);
} elseif($this->session->getFlashBag()->has($name)) {
return $this->session->getFlashBag()->get($name);
} else {
return $this->flashes[$name];
}
}
/**
* Merge all flashBag and $this->flashes values and return the array
*
* #return array
*/
public function all()
{
return array_merge_recursive($this->session->getFlashBag()->all(), $this->flashes);
}
}
NotifyExtension.php
Located at: Game/MainBundle/DependencyInjection/NotifyExtension.php
<?php
namespace Game\MainBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class NotifyExtension extends Extension
{
/**
* {#inheritDoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
Configuration.php
Located at: Game/MainBundle/DependencyInjection/Configuration.php
<?php
namespace Game\MainBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files
*
* To learn more see {#link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
*/
class Configuration implements ConfigurationInterface
{
/**
* {#inheritDoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('game_main');
// Here you should define the parameters that are allowed to
// configure your bundle. See the documentation linked above for
// more information on that topic.
return $treeBuilder;
}
}
Services.yml
Located at: Game/MainBundle/Resources/Config/services.yml
parameters:
game.notify.class: Game\MainBundle\Controller\NotifyController
services:
game.notify:
class: "%game.notify.class%"
arguments:
session: #session
PageController.php
Located at: Game/MainBundle/Controller/PageController.php
<?php
namespace Game\MainBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class PageController extends Controller
{
public function indexAction()
{
$notify = $this->get("game.notify");
$notify->add("test", array("type" => "instant", "message" => "This is awesome"));
if ($notify->has("test")) {
return array("notifications" => $notify->get("test"));
}
return $this->render('GameMainBundle:Page:index.html.twig');
}
}
Based on your answer to my first comment, it would appear that your services are never being loaded due to not following the naming convention for your extension class.
If you have a GameMainBundle for your bundle then you should have GameMainExtension for your extension.
More info here: http://symfony.com/doc/current/cookbook/bundles/best_practices.html
You might still have some problems once you get services.yml loaded. Calling your service a controller is a bit non-standard. But see what happens.

Resources