Here's how you can use Doctrine entities located in a database that's different from the default Concrete CMS one.
Tell Concrete to stay away from your own entities
First of all, you need to be sure that Concrete doesn't add your entities to the standard entity set it manages.
In your package controller.php
add this line right after the namespace declaration:
use Concrete\Core\Database\EntityManager\Provider\ProviderInterface;
and declare that the package controller implements the ProviderInterface
interface:
class Controller extends Package implements ProviderInterface
finally, define a new getDrivers
method that returns an empty array:
public function getDrivers()
{
return [];
}
NOTE: if you also have Doctrine entities that should be managed by Concrete, you need to keep the two kinds of entities in two distict directories.
For instance, you may have in src/Entities/Standard
the entities that should be fully handled by Concrete, and in src/Entities/Custom
the entities that live in a different database.
In this case, the getDrivers
method should return a DriverInterface
instance that tells Concrete to use the entities in the src/Entities/Standard
directory.
Create your own connection
Next you have to define the connection to the database that contains your entities. See this documentation page to know how to add it.
Let's allume that you'll name the connection as my-connection
.
Create your own Entity Manager
You can define your own entity manager in the src
directory of your package.
<?php
namespace MyNamespace;
use Concrete\Core\Database\DatabaseStructureManager;
use Doctrine\ORM\EntityManager;
class MyEntityManager extends EntityManager
{
/**
* Refresh the proxy classes.
*/
public function refreshProxyClasses()
{
$manager = new DatabaseStructureManager($this);
$manager->clearCacheAndProxies();
}
}
The refreshProxyClasses
method proposed here is just an example. If executed, it will only refresh the doctrine proxy classes without touching the database structure (it's very useful if your database structure is shared between other projects and it must not be changed by Concrete).
Initialize your own Entity Manager
Then, in your package controller.php
, you can add a function that initializes your the entity manager:
private function registerEntityManager()
{
$this->app->singleton(\MyNamespace\MyEntityManager::class, function ($app) {
$driver = new AnnotationDriver($app->make('orm/cachedAnnotationReader'), [DIR_BASE . '/' . DIRNAME_PACKAGES . '/my_package_handle/src/Entity']);
$driverChain = new MappingDriverChain();
$driverChain->addDriver($driver, 'MyNamespace\Entity');
$ormConfiguration = $app->make(\Doctrine\ORM\Configuration::class);
$ormConfiguration->setMetadataDriverImpl($driverChain);
$databaseManager = $app->make(\Concrete\Core\Database\DatabaseManager::class);
$connection = $databaseManager->connection('my-connection');
$entityManager = new \MyNamespace\MyEntityManager($connection, $ormConfiguration, $connection->getEventManager());
return $entityManager;
});
}
public function on_start()
{
$this->registerEntityManager();
}
In this example, the custom entity manager will use the standard annotation driver, and the entities will be placed in the src/Entity
folder under your package.
Proxy class generation
When your package is installed or upgraded, you may need to create or refresh the proxy classes.
In order to do so, simply add these two method to your package controller.php
:
public function install()
{
$this->registerEntityManager();
$this->refreshProxyClasses();
}
public function upgrade()
{
$this->registerEntityManager();
}
private function registerEntityManager()
{
$this->app->make(\MyNamespace\MyEntityManager::class)->refreshProxyClasses();
}
Integration with Concrete
Since Concrete 8.3, it's possible to list your own Doctrine entities in the /dashboard/system/environment/entities
dashboard page, as well as refreshing the proxy classes when users hit the Refresh Entities
in that page.
Just add this method to your package controller.php
file and call it from the on_start
method:
private function registerEntityEvents()
{
$app = $this->app;
$director = $app->make(\Symfony\Component\EventDispatcher\EventDispatcher::class);
$director->addListener('on_list_package_entities', function ($event) use ($app) {
$event->addEntityManager($app->make(\MyNamespace\MyEntityManager::class));
});
$director->addListener('on_refresh_package_entities', function ($event) use ($app) {
$app->make(\MyNamespace\MyEntityManager::class)->refreshProxyClasses();
});
}