Retour d'expérience - Veolys vs ProwebCE
Clément Bertillon
D'une migration progressive
Un outils de gestion d'ensemble immobilier
Backoffice HM et refonte du SI ProwebCE
1er commit git- Gilles 2007
(une précédente version éxistait)
Legacy: 200 000 lignes de codes
Symfony3: 60 000 lignes de codes
500 routes hors webservice/api
Phpmetrics
Veolys : 61674 lignes
BPM : 61730 lignes
PHP 5.3
Apache 2.2
Mysql 5.5
Memcache
PHP 7
Apache 2.4
Percona Server - Mysql 5.7
Redis
symfony 1.0 (beta) avec vendor commité
Mot de passe en clair parce que c'est plus facile à lire/modifier pour le client
Un webservice en SOAP, avec un seul point d'entré qui accepte un fichier XML sans règles de validation
clement.bertillon - Francoisgueguen - Jules Pietri - Jérémy DERUSSE - Jérémy Derussé - Loïc Chardonnet - Mathieu MARCHOIS - Michel ROCA D'HUYTEZA - Olga ROTACH - PaskR - Quentin GUILLEMINEAU - Romain GAUTIER - Salah MEHARGA - Sylvain Jaune - alexandre.salome - fabien - fred - fx.deguillebon - geoffrey.bachelet - gilles - gregoire.hubert - gregoire.pineau - illusionOfParadise - inal.djafar - itkg-nanne - jeremy.derusse - julien.levasseur - loic.chardonnet - mRoca - mathieu.cavanne - maxime.douailin - pierre.cahard - romain.dorgueil - romain.gautier - roupen.torossian - sensio - simon.franquet - sofany.ong - thibaut.prim - vincent.dravert - xavier.briand - yohann.giarelli
git shortlog --summary
Christophe NGUYEN, Philippe MARQUINE, Stephane THEVENOUD, Philippe BEAUVAIS, Thibault DANTIGNY, Hugues GOBET, akeriel, bkaciousalah, Jonathan LEQUIN, Damien OGIER-DENIS, stephane thevenoud, Patrick SIMON, Adrien MADET, Antonin CLAUZIER, fmartin, Catherine GROUBON, Patrick CLOUZEAU, camille hernoux, Jonathan Lequin, François MARTIN-BROSSAT, Camille HERNOUX, Stephane BIZOLLON, ppailley, vmasia, kevin renaud, Ludovic LASQUELLEC, Amandine THERY, Adrien Madet, Jean-Jacques Parrenin, Ousmane KANTE, Hugues HG. GOBET, Jean Jacques Parrenin, marecauxg, Franck Sun, Christophe Nguyen, DIDIER UNGAR, gstorchi, Ousmane KANTE, Hugues Gobet, laurenta, Jean-Jacques PARRENIN, Damien LORTHIOIS, Francois MARTIN-BROSSAT, Antonin Clauzier, bialoruckio, Jean-Marie Le Bonniec (externe - smile), Yani Makouf (ext), Kevin RENAUD, traissardjm, Kévin ARBOUIN, Linda Bouzid, Boris FOURNIER, Maxime BERNARD, Zakaria Harrou, Fabien PIRON, Kevin ARBOUIN, Franck SUN, Christophe PYREE, Mehdi Tebourbi, Zakaria HARROU, llasquellec, Linda BOUZID, Thomas TALBOT, Mahmoud CHARFEDDINE, Jenkins HM, Ulas ATILA, asabbagh-e, Julien VANDROMME, Christophe DENIVET, Anis SABBAGH, Thibault Dantigny, anefoussi-e, Laetitia MONTEFERRARIO, perassia, Tayeb CHIKHI, Hakim BOUCHEMA, Kevin Renaud, Hugo Mallaroni-Cosentino, Yani Makouf, Ali CANSEV, agkerros, adminweb, Hugo MALLARONI-COSENTINO, Clement Bertillon, Antoine DI BARTOLOMEO, Boris Fournier, Jean Jacques Parrenin, Seifallah Azzabi, Di Bartolomeo, administrateur, Stéphanie SAINT-MARCEL, Alexandre VINET, Leo RIGER, Administrator, Stephane BEKALE, Thomas Talbot, Smaine Milianni, Emilie BAUDUIN, Seifallah AZZABI, Lara DUFOUR, clement.bertillon, cvs2svn, ddelot, Pascal BUISSON, Julien Manganne, Aroussi NEFOUSSI, Julien Maulny, pmichalet, tchikhi-e, Thibault, Yani MAKOUF, Charlie ARTO, Smaine MILIANNI, Badr HAKKARI, Safouane RKIK, Clement Passevant, Safouane REKIK, apaixao, Romain Gautier, root, ZaKaRiA, Christophe DEVINET, Stephane BEKALE, Fabien PAPET
Christophe NGUYEN, Philippe MARQUINE, Stephane THEVENOUD, Philippe BEAUVAIS, Thibault DANTIGNY, Hugues GOBET, akeriel, bkaciousalah, Jonathan LEQUIN, Damien OGIER-DENIS, stephane thevenoud, Patrick SIMON, Adrien MADET, Antonin CLAUZIER, fmartin, Catherine GROUBON, Patrick CLOUZEAU, camille hernoux, Jonathan Lequin, François MARTIN-BROSSAT, Camille HERNOUX, Stephane BIZOLLON, ppailley, vmasia, kevin renaud, Ludovic LASQUELLEC, Amandine THERY, Adrien Madet, Jean-Jacques Parrenin, Ousmane KANTE, Hugues HG. GOBET, Jean Jacques Parrenin, marecauxg, Franck Sun, Christophe Nguyen, DIDIER UNGAR, gstorchi, Ousmane KANTE, Hugues Gobet, laurenta, Jean-Jacques PARRENIN, Damien LORTHIOIS, Francois MARTIN-BROSSAT, Antonin Clauzier, bialoruckio, Jean-Marie Le Bonniec (externe - smile), Yani Makouf (ext), Kevin RENAUD, traissardjm, Kévin ARBOUIN, Linda Bouzid, Boris FOURNIER, Maxime BERNARD, Zakaria Harrou, Fabien PIRON, Kevin ARBOUIN, Franck SUN, Christophe PYREE, Mehdi Tebourbi, Zakaria HARROU, llasquellec, Linda BOUZID, Thomas TALBOT, Mahmoud CHARFEDDINE, Jenkins HM, Ulas ATILA, asabbagh-e, Julien VANDROMME, Christophe DENIVET, Anis SABBAGH, Thibault Dantigny, anefoussi-e, Laetitia MONTEFERRARIO, perassia, Tayeb CHIKHI, Hakim BOUCHEMA, Kevin Renaud, Hugo Mallaroni-Cosentino, Yani Makouf, Ali CANSEV, agkerros, adminweb, Hugo MALLARONI-COSENTINO, Clement Bertillon, Antoine DI BARTOLOMEO, Boris Fournier, Jean Jacques Parrenin, Seifallah Azzabi, Di Bartolomeo, administrateur, Stéphanie SAINT-MARCEL, Alexandre VINET, Leo RIGER, Administrator, Stephane BEKALE, Thomas Talbot, Smaine Milianni, Emilie BAUDUIN, Seifallah AZZABI, Lara DUFOUR, clement.bertillon, cvs2svn, ddelot, Pascal BUISSON, Julien Manganne, Aroussi NEFOUSSI, Julien Maulny, pmichalet, tchikhi-e, Thibault, Yani MAKOUF, Charlie ARTO, Smaine MILIANNI, Badr HAKKARI, Safouane RKIK, Clement Passevant, Safouane REKIK, apaixao, Romain Gautier, root, ZaKaRiA, Christophe DEVINET, Stephane BEKALE, Fabien PAPET
Christophe NGUYEN, Philippe MARQUINE, Stephane THEVENOUD, Philippe BEAUVAIS, Thibault DANTIGNY, Hugues GOBET, akeriel, bkaciousalah, Jonathan LEQUIN, Damien OGIER-DENIS, stephane thevenoud, Patrick SIMON, Adrien MADET, Antonin CLAUZIER, fmartin, Catherine GROUBON, Patrick CLOUZEAU, camille hernoux, Jonathan Lequin, François MARTIN-BROSSAT, Camille HERNOUX, Stephane BIZOLLON, ppailley, vmasia, kevin renaud, Ludovic LASQUELLEC, Amandine THERY, Adrien Madet, Jean-Jacques Parrenin, Ousmane KANTE, Hugues HG. GOBET, Jean Jacques Parrenin, marecauxg, Franck Sun, Christophe Nguyen, DIDIER UNGAR, gstorchi, Ousmane KANTE, Hugues Gobet, laurenta, Jean-Jacques PARRENIN, Damien LORTHIOIS, Francois MARTIN-BROSSAT, Antonin Clauzier, bialoruckio, Jean-Marie Le Bonniec (externe - smile), Yani Makouf (ext), Kevin RENAUD, traissardjm, Kévin ARBOUIN, Linda Bouzid, Boris FOURNIER, Maxime BERNARD, Zakaria Harrou, Fabien PIRON, Kevin ARBOUIN, Franck SUN, Christophe PYREE, Mehdi Tebourbi, Zakaria HARROU, llasquellec, Linda BOUZID, Thomas TALBOT, Mahmoud CHARFEDDINE, Jenkins HM, Ulas ATILA, asabbagh-e, Julien VANDROMME, Christophe DENIVET, Anis SABBAGH, Thibault Dantigny, anefoussi-e, Laetitia MONTEFERRARIO, perassia, Tayeb CHIKHI, Hakim BOUCHEMA, Kevin Renaud, Hugo Mallaroni-Cosentino, Yani Makouf, Ali CANSEV, agkerros, adminweb, Hugo MALLARONI-COSENTINO, Clement Bertillon, Antoine DI BARTOLOMEO, Boris Fournier, Jean Jacques Parrenin, Seifallah Azzabi, Di Bartolomeo, administrateur, Stéphanie SAINT-MARCEL, Alexandre VINET, Leo RIGER, Administrator, Stephane BEKALE, Thomas Talbot, Smaine Milianni, Emilie BAUDUIN, Seifallah AZZABI, Lara DUFOUR, clement.bertillon, cvs2svn, ddelot, Pascal BUISSON, Julien Manganne, Aroussi NEFOUSSI, Julien Maulny, pmichalet, tchikhi-e, Thibault, Yani MAKOUF, Charlie ARTO, Smaine MILIANNI, Badr HAKKARI, Safouane RKIK, Clement Passevant, Safouane REKIK, apaixao, Romain Gautier, root, ZaKaRiA, Christophe DEVINET, Stephane BEKALE, Fabien PAPET
Christophe NGUYEN, Philippe MARQUINE, Stephane THEVENOUD, Philippe BEAUVAIS, Thibault DANTIGNY, Hugues GOBET, akeriel, bkaciousalah, Jonathan LEQUIN, Damien OGIER-DENIS, stephane thevenoud, Patrick SIMON, Adrien MADET, Antonin CLAUZIER, fmartin, Catherine GROUBON, Patrick CLOUZEAU, camille hernoux, Jonathan Lequin, François MARTIN-BROSSAT, Camille HERNOUX, Stephane BIZOLLON, ppailley, vmasia, kevin renaud, Ludovic LASQUELLEC, Amandine THERY, Adrien Madet, Jean-Jacques Parrenin, Ousmane KANTE, Hugues HG. GOBET, Jean Jacques Parrenin, marecauxg, Franck Sun, Christophe Nguyen, DIDIER UNGAR, gstorchi, Ousmane KANTE, Hugues Gobet, laurenta, Jean-Jacques PARRENIN, Damien LORTHIOIS, Francois MARTIN-BROSSAT, Antonin Clauzier, bialoruckio, Jean-Marie Le Bonniec (externe - smile), Yani Makouf (ext), Kevin RENAUD, traissardjm, Kévin ARBOUIN, Linda Bouzid, Boris FOURNIER, Maxime BERNARD, Zakaria Harrou, Fabien PIRON, Kevin ARBOUIN, Franck SUN, Christophe PYREE, Mehdi Tebourbi, Zakaria HARROU, llasquellec, Linda BOUZID, Thomas TALBOT, Mahmoud CHARFEDDINE, Jenkins HM, Ulas ATILA, asabbagh-e, Julien VANDROMME, Christophe DENIVET, Anis SABBAGH, Thibault Dantigny, anefoussi-e, Laetitia MONTEFERRARIO, perassia, Tayeb CHIKHI, Hakim BOUCHEMA, Kevin Renaud, Hugo Mallaroni-Cosentino, Yani Makouf, Ali CANSEV, agkerros, adminweb, Hugo MALLARONI-COSENTINO, Clement Bertillon, Antoine DI BARTOLOMEO, Boris Fournier, Jean Jacques Parrenin, Seifallah Azzabi, Di Bartolomeo, administrateur, Stéphanie SAINT-MARCEL, Alexandre VINET, Leo RIGER, Administrator, Stephane BEKALE, Thomas Talbot, Smaine Milianni, Emilie BAUDUIN, Seifallah AZZABI, Lara DUFOUR, clement.bertillon, cvs2svn, ddelot, Pascal BUISSON, Julien Manganne, Aroussi NEFOUSSI, Julien Maulny, pmichalet, tchikhi-e, Thibault, Yani MAKOUF, Charlie ARTO, Smaine MILIANNI, Badr HAKKARI, Safouane RKIK, Clement Passevant, Safouane REKIK, apaixao, Romain Gautier, root, ZaKaRiA, Christophe DEVINET, Stephane BEKALE, Fabien PAPET
On garde un ensemble de contraintes qui vont faciliter la migration
- Les mêmes URL
- La même version de PHP
- Le même server
- Le même domaine
Maintenir une application PHP tiers au sein d'une application Symfony
<?php
// web/index.php
require_once __DIR__.'/../Symfony2/app/bootstrap.php.cache';
require_once __DIR__.'/../Symfony2/app/StaticKernel.php';
$staticKernel = StaticKernel::getInstance();
$request = Request::createFromGlobals();
$matcher = $staticKernel->getUrlMatcher($request);
try {
$attributes = $matcher->match($request->getPathInfo());
} catch (ResourceNotFoundException $e) {
chdir(__DIR__.'/../symfony1/web');
require_once __DIR__.'/../symfony1/web/index.php';
return;
} catch (MethodNotAllowedException $e) {
}
$kernel = $staticKernel->getKernel();
$kernel->loadClassCache();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
Symfony2
Symfony2
symfony1
Symfony3
Supporte: symfony1.4 / symfony1.5 / CodeIgniter
et custom kernel
Symfony4 cherche un controller
qui match la request
HTTP Request
Si aucune route Sf4, on passe la main au legacy pour faire le même travail
/**
* @param GetResponseEvent $event
*
* @return GetResponseEvent
*/
public function onKernelRequest(GetResponseEvent $event)
{
try {
$this->routerListener->onKernelRequest($event);
} catch (NotFoundHttpException $e) {
// Log ...
$response = $this->legacyKernel->handle($event->getRequest(),
$event->getRequestType(), true);
if ($response->getStatusCode() !== 404) {
$event->setResponse($response);
return $event;
}
}
}
class Symfony10Kernel extends LegacyKernel
{
/** @var ContainerInterface */
private $container;
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
{
$context = $this->setUpContext($request);
ob_start();
// Dispatch request
try {
$context->getController()->dispatch();
} catch (\sfStopException $e) {
} catch (\Exception $e) {
throw $e;
} finally {
$context->shutdown();
}
$stdout = ob_get_contents();
ob_end_clean();
return $this->convertResponse($context->getResponse(), $stdout);
}
// ...
}
// \sfContext
class ProwebLegacyKernel extends LegacyKernel
{
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
{
// There is 3 type of legacy: Zend, homemade Framework and procedural php
$legacyType = $this->getLegacyType($request->getUri());
// Start the buffer and call the right legacy
ob_start();
switch ($legacyType) {
case self::LEGACY_TYPE_HOMEPAGE:
require_once $this->getRootDir().'/../legacy/hm.php';
break;
case self::LEGACY_TYPE_ZEND:
require_once $this->getRootDir().'/../legacy/hm2/public/index.php';
break;
case self::LEGACY_TYPE_HOMEMADE_FRAMEWORK:
require_once $this->getRootDir().'/../legacy/front.php';
break;
case self::LEGACY_TYPE_PROCEDURAL_PHP:
putenv('PHP_SELF=/'.implode('/', $pathParts));
require_once $this->getRootDir().'/../legacy/'.implode('/', $pathParts);
break;
}
$response = ob_get_clean();
return new Response($response, 200, $header);
}
}
# app/config/config.yml
theodo_evolution_legacy_wrapper:
root_dir: %kernel.root_dir%/../legacy
kernel:
id: legacy.kernel.symfony10
options:
application: frontend
environment: '%kernel.environment%'
debug: '%kernel.debug%'
Lecture de session: TheodoEvolutionSessionBundle
Authentification:
PreAuthenticatedListener custom
Authentification géré par l'application legacy
Supporte: symfony1.x / CodeIgniter
class BagConfiguration implements BagManagerConfigurationInterface
{
private $namespaces = array(
BagManagerConfigurationInterface::LAST_REQUEST_NAMESPACE => 'symfony/user/sfUser/lastRequest',
BagManagerConfigurationInterface::AUTH_NAMESPACE => 'symfony/user/sfUser/authenticated',
BagManagerConfigurationInterface::CREDENTIAL_NAMESPACE => 'symfony/user/sfUser/credentials',
BagManagerConfigurationInterface::ATTRIBUTE_NAMESPACE => 'symfony/user/sfUser/attributes',
BagManagerConfigurationInterface::CULTURE_NAMESPACE => 'symfony/user/sfUser/culture',
);
/**
* {@inheritdoc}
*/
public function getNamespaces()
{
return $this->namespaces;
}
/**
* {@inheritdoc}
*/
public function getNamespace($key)
{
return $this->namespaces[$key];
}
}
namespace Veolys\LegacyBundle\Security\Firewall;
/**
* Retrieves the user ID from the symfony1 session.
*/
class Symfony1SessionListener extends AbstractPreAuthenticatedListener
{
/**
* Gets the user and credentials from the Request.
*/
protected function getPreAuthenticatedData(Request $request)
{
if (!$this->veolysSession->isAuthenticated()) {
// For call the entryPoint and redirect the user to /
throw new BadCredentialsException();
}
return [(string) $this->veolysSession->getUserId(), null];
}
}
mysql> select id, name from veolys_permission;
+-----+-------------------------------------------------+
| id | name |
+-----+-------------------------------------------------+
| 1 | Building/Edit |
| 3 | HotlineRequest/View |
| 5 | HotlineRequest/ViewAll |
| 6 | HotlineRequest/Post |
| 7 | HotlineRequest/PostForAll |
| 8 | HotlineRequest/DetailsChangeDescription |
| 9 | HotlineRequest/DetailsDecideConclusion |
| 16 | HotlineRequest/IsDispatcher |
# ...
| 158 | HotlineRequest/DelayAlert |
| 159 | HotlineRequest/Indicators |
| 160 | HotlineRequest/Lots |
+-----+-------------------------------------------------+
149 rows in set (0.00 sec)
namespace Veolys\LegacyBundle\Security\Authentication\Provider;
/**
* Retrieves the user and his rights from the database
*/
class Symfony1SessionProvider implements AuthenticationProviderInterface
{
public function authenticate(TokenInterface $token)
{
$username = $token->getUsername();
$user = $this->userProvider
->loadUserByUsername($username) // l'Id qui vient du PreAuthenticatedListener
;
if (!$user) {
throw new AuthenticationException('The symfony1 session authentication failed.');
}
$roles = $this->em
->getRepository('VeolysApplicationBundle:VeolysUser')
->getRoles($user)
;
$user->setRoles($roles);
$authenticatedToken = new PreAuthenticatedToken($user, null, 'veolys', $roles);
return $authenticatedToken;
}
}
public function getRoles($user)
{
$permissions = $this->getUserPermissions();
$roles = array_map(
function ($permission) {
preg_match_all('#((?:^|[A-Z])[a-z]+)/?#', $permission['name'], $matches);
$name = strtoupper(implode('_', $matches[1]));
return 'ROLE_'.$name;
},
$permissions
);
if ($user->isSuperAdmin()) {
$roles[] = 'ROLE_SUPER_ADMIN';
}
$roles[] = 'ROLE_USER';
return $roles;
}
HotlineRequest/Post => ROLE_HOTLINE_REQUEST_POST
/**
* @Route("/edit", name="i_love_veolys")
*
* @Security("has_role('ROLE_HOTLINE_REQUEST_POST')")
*/
public function editAction(Request $request): Response
{
}
Base legacy
Nouvelle base
class RoleDecoder
{
/**
* Encode an array to a string
* [
* ROLE_CE_LISTE_READ_ONLY,
* ROLE_GESTION_PASSERELLE_UPDATE,
* ROLE_GESTION_MARQUE_BLANCHE_UPDATE,
* ].
*
* "|ce_liste=c|gestion_passerelle=m|gestion_marque_blanche=m|"
*/
public function encode(array $roles): string
{
$legacyRight = self::BEGIN_LEGACY_PERMISSION;
foreach ($roles as $role) {
$rolesToAdd[] = $role;
foreach ($rolesToAdd as $roleToAdd) {
$roleToAdd = str_replace('ROLE_', '', $roleToAdd);
if (0 < strpos($roleToAdd, '_READ_ONLY')) {
$legacyRight .= self::formatterLegacyPermission($roleToAdd, '_READ_ONLY',
strtolower('C'));
} elseif (0 < strpos($roleToAdd, '_UPDATE')) {
$legacyRight .= self::formatterLegacyPermission($roleToAdd, '_UPDATE',
strtolower('M'));
}
}
}
return $legacyRight;
}
}
// Legacy
"|onglet_parametres_acances=m|ce_liste=c|"
// Symfony4
['ROLE_ONGLET_PARAMETRES_AVANCES_UPDATE', 'ROLE_CE_LISTE_READ_ONLY']
Reverse engineering du legacy (symfony1)
Estimation: il faudra {{ random(20) + 20 }} jours
+ 3 jours de récup
Pas de migration, on va appeler le legacy
// src/LegacyBundle/Kernel/Symfony10Kernel.php
class Symfony10Kernel extends LegacyKernel
{
public function handle(Request $request)
{
}
public function notify($object)
{
}
// ...
}
Comment ?
Identifier le code legacy que l'on souhaite appeler
// legacy/plugins/appVeolysHotlineModule/lib/model/HotlineRequest.php
public function notifyChange()
{
// ...
$broadcastList = $this->getOrCreateUserBroadcastList();
$broadcastList->notify($this)
}
// src/LegacyBundle/Kernel/Symfony10Kernel.php
class Symfony10Kernel extends LegacyKernel
{
/**
* Call the notify method from the legacy UserBroadCastList model.
*/
public function notify($objectSF4)
{
// Map the doctrine entity to the propel model
$legacyObject = $this->transformToLegacy($objectSF4);
// Call the symfony1 method to get a UserBroadcastList
$userBroadCastList = $legacyObject->getOrCreateUserBroadcastList();
// Call the legacy method notify
try {
$broadCastList->notify($legacyObject);
} catch (\Exception $e) {
}
}
// ...
}
namespace AppBundle\Event;
class NotifyObjectEvent extends Event
{
const NAME = 'veolys.notify_object';
protected $object;
public function __construct(NotifiableObjectInterface $object)
{
$this->object = $object;
}
public function getObject()
{
return $this->object;
}
}
class NotifyObjectListener implements EventSubscriberInterface
{
/** @var LoggerInterface */
protected $logger;
/** @var Symfony10Kernel */
protected $kernel;
/** @var Request */
protected $request;
public function notify(NotifyObjectEvent $event)
{
$object = $event->getObject();
if (!$this->supports($object)) {
return;
}
$this->kernel->notify($this->request, $object);
$this->logger->notice('Legacy notification', [
'object' => get_class($object),
]);
}
public function supports($object): bool
{
return $object instanceof NotifiableObjectInterface;
}
}
// src/LegacyBundle/Kernel/Symfony10Kernel.php
class Symfony10Kernel extends LegacyKernel
{
/**
* Call the notify method from the legacy UserBroadCastList model.
*/
public function notify(Request $request, NotifiableObjectInterface $object)
{
// Map the doctrine entity to the propel model
$legacyObject = $this->transformToLegacy($object);
// Call the symfony1 method to get a UserBroadcastList
$userBroadCastList = $legacyObject->getOrCreateUserBroadcastList();
// Call the legacy method notify
try {
$broadCastList->notify($legacyObject);
} catch (\Exception $e) {
}
}
// ...
}
/** @var NotifiableObjectInterface $object */
$id = $object->getId();
$legacyRepositoryName = sprintf('%sPeer', (new \ReflectionClass($object))->getShortName());
if (!class_exists($legacyRepositoryName)) {
throw new \Exception(sprintf('Invalid legacy repository peer : %s', $legacyRepositoryName));
}
$legacyObject = $legacyRepositoryName::retrieveByPK($id);
if (empty($legacyObject)) {
throw new \Exception(sprintf('Can\'t found the object with id: %s and legacy repository peer: %s',
$id, $legacyRepositoryName));
}
if (!$legacyObject instanceof \Notifiable) {
throw new \Exception(sprintf('The object % should implement Notifiable interface from legacy',
get_class($legacyObject)));
}
// Call the method with Reflection Api because the method getOrCreateUserBroadcastList can be private...
$method = new \ReflectionMethod(get_class($legacyObject), 'getOrCreateUserBroadcastList');
$method->setAccessible(true);
/** @var \UserBroadcastList $broadCastList */
$broadCastList = $method->invoke($legacyObject);
public function notify(Request $request, NotifiableObjectInterface $object)