Commit 9ed48793 authored by Avris's avatar Avris

v2.0

parent ee4b63cd
tests/_output/*
vendor/*
composer.lock
.idea/*
**/.DS_Store
<?php
namespace Avris\Micrus\Doctrine;
use Avris\Micrus\ORM;
use Avris\Micrus\App;
use Avris\Micrus\Doctrine\MicrusRepositoryFactory;
use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\EntityManager;
class Doctrine implements ORM
{
/** @var EntityManager */
protected $entityManager;
public function __construct(App $app, $db)
{
$app->getLogger()->log('info', sprintf('Database driver %s', $db['driver']));
$config = Setup::createAnnotationMetadataConfiguration(
array($app->getRootDir() . '/App/Model'),
$app->getEnv() == 'dev',
null,
null,
false
);
$config->setRepositoryFactory(new MicrusRepositoryFactory());
$this->entityManager = EntityManager::create($db, $config);
}
public function getEntityManager()
{
return $this->entityManager;
}
public function find($type, $id)
{
return $this->entityManager->getRepository($type)->find($id);
}
public function findAll($type)
{
return $this->entityManager->getRepository($type)->findAll();
}
public function findBy($type, $attribute, $value)
{
return $this->entityManager->getRepository($type)->findBy(array($attribute => $value));
}
public function findOneBy($type, $attribute, $value)
{
return $this->entityManager->getRepository($type)->findOneBy(array($attribute => $value));
}
}
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Avris\Micrus\Doctrine;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
class MicrusRepositoryFactory extends DefaultRepositoryFactory
{
/**
* {@inheritDoc}
*/
protected function createRepository(EntityManagerInterface $entityManager, $entityName)
{
if (strpos($entityName, '\\') === false) {
$entityName = 'App\\Model\\' . ucfirst($entityName);
}
return parent::createRepository($entityManager, $entityName);
}
}
\ No newline at end of file
## Micrus Doctrine Bridge ##
This is a module for [Micrus framework](http://micrus.avris.it) that allows you to integrate it with [Doctrine ORM](http://www.doctrine-project.org/projects/orm.html).
To install this module, run:
composer require avris/micrus-doctrine
Then open the file `app/Config/modules.yml` and add:
- Avris\Micrus\Doctrine\DoctrineModule
### Example usage ###
<?php
namespace App\Model;
use Avris\Micrus\Controller\Http\UploadedFile;
use Avris\Micrus\Model\Widget\ChoiceHelper;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="post")
* @ORM\HasLifecycleCallbacks
**/
class Post
{
/**
* @var integer
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @var string
* @ORM\Column(type="string")
*/
protected $title;
/**
* @var string
* @ORM\Column(type="text")
*/
protected $content;
/**
* @var \DateTime
* @ORM\Column(type="datetime")
*/
protected $publishedAt;
/**
* @var User
* @ORM\ManyToOne(targetEntity="User", inversedBy="posts")
**/
protected $user;
/**
* @var Category[]|ArrayCollection
* @ORM\ManyToMany(targetEntity="Category", inversedBy="posts")
* @ORM\JoinTable(name="category_post")
**/
protected $categories;
/**
* @var UploadedFile
*/
protected $attachment;
/**
* @var string
* @ORM\Column(type="string", nullable=true)
*/
protected $file;
public function __construct()
{
$this->categories = new ArrayCollection();
$this->publishedAt = new \DateTime();
}
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* @param string $title
* @return $this
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* @return string
*/
public function getContent()
{
return $this->content;
}
/**
* @param string $content
* @return $this
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* @return \DateTime
*/
public function getPublishedAt()
{
return $this->publishedAt;
}
/**
* @param \DateTime $publishedAt
* @return $this
*/
public function setPublishedAt($publishedAt)
{
$this->publishedAt = $publishedAt;
return $this;
}
/**
* @return User
*/
public function getUser()
{
return $this->user;
}
/**
* @param User $user
*/
public function setUser($user)
{
$this->user = $user;
}
/**
* @return Category[]|ArrayCollection
*/
public function getCategories()
{
return $this->categories;
}
/**
* @param Category $category
* @return $this
*/
public function addCategories(Category $category)
{
if (!$this->categories->contains($category)) {
$this->categories->add($category);
}
return $this;
}
/**
* @param Category $category
* @return $this
*/
public function removeCategories(Category $category)
{
$this->categories->removeElement($category);
return $this;
}
/**
* @return UploadedFile
*/
public function getAttachment()
{
return $this->attachment;
}
/**
* @param UploadedFile $attachment
*/
public function setAttachment(UploadedFile $attachment)
{
$this->attachment = $attachment;
}
/**
* @return string
*/
public function getFile()
{
return $this->file;
}
/**
* @ORM\PrePersist
* @ORM\PreUpdate
*/
public function handleFileUpload()
{
if (!$this->attachment) {
return;
}
$filename = substr(md5(uniqid()), 0, 8) . '.' . $this->attachment->getExtension();
if ($this->attachment->moveTo('run/att/' . $filename)) {
$this->file = $filename;
}
}
}
**Note the convention** that is important for binding on object to a form and displaying obejct's values in Twig templates:
if an attribute `$fieldName` is not public, it will be accessed using `getFieldName` and `setFieldName`, or if it's an array:
`getFieldName`, `addFieldName`, `removeFieldName` and `hasFieldName`.
You can use Donctrine's EntityManager, retrieving it with `$this->getEm()` (in controllers) or `$container->get('orm')->getEntityManager()`.
You can fetch repositories simply by the entity's name:
public function incrementStuffAction()
{
$stuff = $this->getEm()->getRepository('Stuff')->findBySomeCustomCondition();
$stuff->incrementValue();
$this->getEm()->persist($stuff);
$this->getEm()->flush();
$this->addFlash('success', 'Stuff incremented successfully');
return $this->redirectToRoute('home');
}
### Console ###
Some Doctrine console commands are available in `bin/micrus` under `db` namespace:
php bin/micrus db:schema:create
php bin/micrus db:schema:update
php bin/micrus db:schema:validate
php bin/micrus db:schema:drop
php bin/micrus db:query:sql "SELECT * FROM user"
php bin/micrus db:query:dql "SELECT u FROM App\Model\User u WHERE u.role='ROLE_ADMIN'"
You can also use `php bin/mdoctrine` to access more advanced commands from Doctrine.
<?php
namespace App\Task;
use Avris\Micrus\Task\Task;
use Avris\Micrus\IO\ArgvInput;
use Avris\Micrus\IO\StdInput;
use Avris\Micrus\IO\StdOutput;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\SchemaTool;
class DoctrineTask extends Task {
public function configure()
{
$this
->addArgument('task')
->addOption('force')
;
}
public function execute(ArgvInput $argv, StdInput $input, StdOutput $output)
{
/** @var EntityManager $em */
$em = $this->getEm();
$schemaTool = new SchemaTool($em);
$metadatas = $em->getMetadataFactory()->getAllMetadata();
$conn = $em->getConnection();
$force = $argv->getOption('force');
if ($this->getApp()->getEnv() != 'dev' && $force) {
$output->writeln('This command can only be executed in dev environment.',
StdOutput::FOREGROUND_WHITE,
StdOutput::BACKGROUND_RED
);
return;
}
switch (strtolower($argv->getArgument('task'))) {
case 'create':
$sqls = $schemaTool->getCreateSchemaSql($metadatas);
break;
case 'update':
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, true);
break;
case 'drop':
$sqls = $schemaTool->getDropSchemaSQL($metadatas);
break;
default:
$output->writeln('Unrecognised command.', StdOutput::FOREGROUND_WHITE, StdOutput::BACKGROUND_RED);
$output->writeln('Available command are: ');
$output->writeln(' * create');
$output->writeln(' * update');
$output->writeln(' * drop');
return;
}
if (!count($sqls)) {
$output->writeln('No queries to execute');
return;
}
if (!$force) {
$output->writeln(implode(';' . PHP_EOL, $sqls) . ';');
$output->writeln();
$output->writeln('No action will be actually taken, until this command is run with --force option.',
StdOutput::FOREGROUND_BLACK,
StdOutput::BACKGROUND_GREEN
);
$output->writeln();
return;
}
$output->writeln(sprintf('Executing %s queries', count($sqls)));
foreach ($sqls as $sql) {
$conn->executeQuery($sql);
}
$output->writeln('Queries were executed successfully');
}
}
\ No newline at end of file
{
"name": "avris/micrus-orm-doctrine",
"name": "avris/micrus-doctrine",
"type": "library",
"description": "Doctrine ORM bridge for the Micrus framework",
"keywords": ["orm","doctrine","php","micrus","database"],
......@@ -7,18 +7,15 @@
"homepage": "http://micrus.avris.it",
"authors": [{
"name": "avris",
"email": "andrzej@avris.it"
"email": "andrzej@avris.it",
"homepage": "http://avris.it"
}],
"require": {
"avris/micrus": "^1.1",
"doctrine/orm": "2.4.*"
"avris/micrus": "^2.0",
"doctrine/orm": "^2.4"
},
"autoload": {
"psr-4": { "Avris\\Micrus\\Doctrine\\": "" }
"psr-4": { "Avris\\Micrus\\Doctrine\\": "src" }
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
}
"bin": ["mdoctrine"]
}
\ No newline at end of file
services:
orm:
class: Avris\Micrus\Doctrine\Doctrine
parameters: [@app, @config.parameters.database]
tasks:
doctrine: Avris\Micrus\Doctrine\Task
\ No newline at end of file
#!/usr/bin/env php
<?php
use Avris\Micrus\App;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Console\ConsoleRunner;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
use Symfony\Component\Console\Application;
set_time_limit(0);
foreach (['', '/..', '/../..', '/../../..'] as $dir) {
if (file_exists($autoloader = __DIR__ . $dir . '/vendor/autoload.php')) {
require_once $autoloader;
break;
}
}
/** @var EntityManager $em */
$em = (new App('cli'))->warmup()->get('orm')->getEntityManager();
$app = new Application('Doctrine');
$helperSet = $app->getHelperSet();
$helperSet->set(new ConnectionHelper($em->getConnection()), 'db');
$helperSet->set(new EntityManagerHelper($em), 'em');
ConsoleRunner::addCommands($app);
$app->run();
\ No newline at end of file
<?php
namespace Avris\Micrus\Doctrine;
use Avris\Micrus\Console\ConsoleEvent;
use Avris\Micrus\Model\ORM;
use Avris\Micrus\Tool\Cache\CacheClearEvent;
use Avris\Micrus\Tool\Cache\CacheWarmupEvent;
use Avris\Micrus\Tool\Logger;
use Avris\Micrus\Tool\ParametersProvider;
use Doctrine\Common\Cache\ClearableCache;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
use Doctrine\ORM\Tools\Setup;
class Doctrine implements ORM, ParametersProvider
{
/** @var EntityManager */
protected $entityManager;
/** @var string */
protected $cacheDir;
public function __construct(Logger $logger, $env, $rootDir, $db)
{
if (empty($db)) {
$logger->warning('No ORM configuration');
$db = ['driver' => 'pdo_sqlite', 'memory' => true];
}
$logger->debug(sprintf('Doctrine ORM, database driver: %s', $db['driver']));
$this->cacheDir = $rootDir . '/run/cache/' . $env . '/doctrine';
$config = Setup::createAnnotationMetadataConfiguration(
[$rootDir . '/app/Model'],
$env == 'dev',
$this->cacheDir,
null,
false
);
$config->setRepositoryFactory(new MicrusRepositoryFactory());
$config->setSQLLogger(new DoctrineLogger($logger));
if (empty($db['charset'])) {
$db['charset'] = 'utf8';
}
$this->entityManager = EntityManager::create($db, $config);
$warmup = $this->cacheWarmupEvent();
if ($warmup) {
$logger->notice($warmup);
}
}
public function getEntityManager()
{
return $this->entityManager;
}
public function find($type, $id)
{
return $this->entityManager->getRepository($type)->find($id);
}
public function findAll($type)
{
return $this->entityManager->getRepository($type)->findAll();
}
public function findBy($type, $attribute, $value)
{
return $this->entityManager->getRepository($type)->findBy([$attribute => $value]);
}
public function findOneBy($type, $attribute, $value)
{
return $this->entityManager->getRepository($type)->findOneBy([$attribute => $value]);
}
public function cacheClearEvent()
{
$config = $this->entityManager->getConfiguration();
$drivers = [
$config->getQueryCacheImpl(),
$config->getMetadataCacheImpl(),
$config->getHydrationCacheImpl(),
];
foreach ($drivers as $driver) {
if ($driver instanceof ClearableCache) {
$driver->deleteAll();
}
}
}
public function cacheWarmupEvent()
{
if (file_exists($this->cacheDir)) {
return false;
}
mkdir($this->cacheDir, 0777, true);
$metadatas = $this->entityManager->getMetadataFactory()->getAllMetadata();
$this->entityManager->getProxyFactory()->generateProxyClasses($metadatas, $this->cacheDir);
return sprintf(
'Metadata for %s generated in %s',
implode(', ', array_map(function($el) { return $el->name; }, $metadatas)),
$this->cacheDir
);
}
public function consoleEvent(ConsoleEvent $event)
{
if (!$this->entityManager) {
return;
}
$app = $event->getApp();
$helperSet = $app->getHelperSet();
$helperSet->set(new ConnectionHelper($this->entityManager->getConnection()), 'db');
$helperSet->set(new EntityManagerHelper($this->entityManager), 'em');
$app->add((new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand())->setName('db:query:sql'));
$app->add((new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand())->setName('db:query:dql'));
$app->add((new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand())->setName('db:schema:create'));
$app->add((new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand())->setName('db:schema:update'));
$app->add((new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand())->setName('db:schema:drop'));
$app->add((new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand())->setName('db:schema:validate'));
$app->add((new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand())->setName('db:generate:proxies'));
}
public function getDefaultParameters()
{
return [
'database' => [
'driver' => 'pdo_mysql',
'host' => '127.0.0.1',
'dbname' => 'micrus',
'user' => 'root',
'password' => null,
'charset' => 'utf8',
],
];
}
}
<?php
namespace App\Task;
use Avris\Micrus\Console\Task\AbstractFixturesTask;
abstract class DoctrineFixturesTask extends AbstractFixturesTask
{
protected function truncateDatabase()
{
$connection = $this->em->getConnection();
$schemaManager = $connection->getSchemaManager();
$query = 'SET FOREIGN_KEY_CHECKS = 0;';
foreach ($schemaManager->listTables() as $table) {
$name = $table->getName();
$query .= 'TRUNCATE ' . $name . ';';
}
$query .= 'SET FOREIGN_KEY_CHECKS = 1;';
$connection->executeQuery($query);
}
}
<?php
namespace Avris\Micrus\Doctrine;
use Avris\Micrus\Tool\Logger;
use Doctrine\DBAL\Logging\SQLLogger;
class DoctrineLogger implements SQLLogger
{
/** @var Logger */
protected $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function startQuery($sql, array $params = null, array $types = null)
{
$this->logger->info($sql . ' | ' . @json_encode($params) . ' | ' . @json_encode($types));
}
public function stopQuery()
{
}
}
<?php
namespace Avris\Micrus\Doctrine;
use Avris\Micrus\Module;
class DoctrineModule implements Module
{
public function extendConfig($env, $rootDir)
{
return [
'services' => [
'orm' => [
'class' => __NAMESPACE__ . '\Doctrine',
'parameters' => ['@logger', '@env', '@rootDir', '@config.parameters.database'],
'events' => ['cacheClear', 'cacheWarmup', 'console'],
'tags' => ['defaultParameters'],
],
'userProvider' => [
'class' => __NAMESPACE__ . '\DoctrineUserProvider',
'parameters' => ['@orm'],
],
],