--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Command/CreateSchemaDoctrineCommand.php Fri Nov 04 15:59:49 2011 +0100
@@ -0,0 +1,125 @@
+<?php
+
+/*
+ * This file is part of the WikiTagBundle package.
+ *
+ * (c) IRI <http://www.iri.centrepompidou.fr/>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace IRI\Bundle\WikiTagBundle\Command;
+
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Output\Output;
+use Doctrine\ORM\Tools\SchemaTool;
+use Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand;
+use Symfony\Bundle\DoctrineBundle\Command\Proxy\DoctrineCommandHelper;
+
+/**
+ * Command to execute the SQL needed to generate the database schema for
+ * a given entity manager.
+ *
+ * This file is a direct adaptation of the Symfony\Bundle\DoctrineBundle\Command\Proxy\CreateSchemaDoctrineCommand
+ *
+ */
+class CreateSchemaDoctrineCommand extends CreateCommand implements ContainerAwareInterface
+{
+ protected function configure()
+ {
+ parent::configure();
+
+ $this
+ ->setName('wikitag:schema:create')
+ ->setDescription('Executes (or dumps) the SQL needed to generate the database schema')
+ ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command')
+ ->setHelp(<<<EOT
+The <info>doctrine:schema:create</info> command executes the SQL needed to
+generate the database schema for the default entity manager:
+
+<info>php app/console doctrine:schema:create</info>
+
+You can also generate the database schema for a specific entity manager:
+
+<info>php app/console doctrine:schema:create --em=default</info>
+
+Finally, instead of executing the SQL, you can output the SQL:
+
+<info>php app/console doctrine:schema:create --dump-sql</info>
+EOT
+ );
+ }
+
+ /**
+ * @var ContainerInterface
+ */
+ private $container;
+
+ protected function getContainer()
+ {
+ if (null === $this->container) {
+ $this->container = $this->getApplication()->getKernel()->getContainer();
+ }
+
+ return $this->container;
+ }
+
+ /**
+ * @see ContainerAwareInterface::setContainer()
+ */
+ public function setContainer(ContainerInterface $container = null)
+ {
+ $this->container = $container;
+ }
+
+
+ protected function filterCreateSchema($sqls)
+ {
+
+ // get service
+ $schema_utils = $this->getContainer()->get("wikitag.shema_utils");
+
+ $res_sqls = $schema_utils->filter_foreign_key($sqls);
+ $res_sqls = $schema_utils->filter_index_creation($res_sqls);
+
+
+ return $res_sqls;
+ }
+
+ protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas)
+ {
+ $output->write('ATTENTION: This operation should not be executed in a production environment.' . PHP_EOL . PHP_EOL);
+ $sqls = $schemaTool->getCreateSchemaSql($metadatas);
+
+ $createSchemaSql = $this->filterCreateSchema($sqls);
+
+ if ($input->getOption('dump-sql') === true) {
+
+ $output->write(implode(';' . PHP_EOL, $createSchemaSql) . PHP_EOL);
+ } else {
+ $output->write('Creating database schema...' . PHP_EOL);
+ $emHelper = $this->getHelper('em');
+
+ $conn = $emHelper->getEntityManager()->getConnection();
+
+ foreach ($createSchemaSql as $sql) {
+ $conn->executeQuery($sql);
+ }
+ $output->write('Database schema created successfully!' . PHP_EOL);
+ }
+
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
+
+ parent::execute($input, $output);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Command/UpdateSchemaDoctrineCommand.php Fri Nov 04 15:59:49 2011 +0100
@@ -0,0 +1,154 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace IRI\Bundle\WikiTagBundle\Command;
+
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Output\Output;
+use Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand;
+use Doctrine\ORM\Tools\SchemaTool;
+use Symfony\Bundle\DoctrineBundle\Command\Proxy\DoctrineCommandHelper;
+
+
+/**
+ * Command to generate the SQL needed to update the database schema to match
+ * the current mapping information.
+ *
+ * This file is a direct adaptation of the Symfony\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand
+ *
+ */
+class UpdateSchemaDoctrineCommand extends UpdateCommand implements ContainerAwareInterface
+{
+ protected function configure()
+ {
+ parent::configure();
+
+ $this
+ ->setName('wikitag:schema:update')
+ ->setDescription('Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata')
+ ->addOption('em', null, InputOption::VALUE_OPTIONAL, 'The entity manager to use for this command')
+ ->setHelp(<<<EOT
+The <info>doctrine:schema:update</info> command generates the SQL needed to
+synchronize the database schema with the current mapping metadata of the
+default entity manager.
+
+For example, if you add metadata for a new column to an entity, this command
+would generate and output the SQL needed to add the new column to the database:
+
+<info>php app/console doctrine:schema:update --dump-sql</info>
+
+Alternatively, you can execute the generated queries:
+
+<info>php app/console doctrine:schema:update --force</info>
+
+You can also update the database schema for a specific entity manager:
+
+<info>php app/console doctrine:schema:update --em=default</info>
+EOT
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ DoctrineCommandHelper::setApplicationEntityManager($this->getApplication(), $input->getOption('em'));
+
+ parent::execute($input, $output);
+ }
+
+ /**
+ * @var ContainerInterface
+ */
+ private $container;
+
+ protected function getContainer()
+ {
+ if (null === $this->container) {
+ $this->container = $this->getApplication()->getKernel()->getContainer();
+ }
+
+ return $this->container;
+ }
+
+ /**
+ * @see ContainerAwareInterface::setContainer()
+ */
+ public function setContainer(ContainerInterface $container = null)
+ {
+ $this->container = $container;
+ }
+
+
+ protected function filterUpdateSchema($sqls)
+ {
+
+ // get service
+ $schema_utils = $this->getContainer()->get("wikitag.shema_utils");
+
+ $res_sqls = $schema_utils->filter_foreign_key($sqls);
+ $res_sqls = $schema_utils->filter_index_creation($res_sqls);
+
+
+ return $res_sqls;
+ }
+
+ protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas)
+ {
+ // Defining if update is complete or not (--complete not defined means $saveMode = true)
+ $saveMode = ($input->getOption('complete') !== true);
+
+ $sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
+ $sqls = $this->filterUpdateSchema($sqls);
+ if (0 == count($sqls)) {
+ $output->writeln('Nothing to update - your database is already in sync with the current entity metadata.');
+
+ return;
+ }
+
+ $dumpSql = (true === $input->getOption('dump-sql'));
+ $force = (true === $input->getOption('force'));
+ if ($dumpSql && $force) {
+ throw new \InvalidArgumentException('You can pass either the --dump-sql or the --force option (but not both simultaneously).');
+ }
+
+ if ($dumpSql) {
+ $output->writeln(implode(';' . PHP_EOL, $sqls));
+ } else if ($force) {
+ $output->writeln('Updating database schema...');
+
+ $emHelper = $this->getHelper('em');
+
+ $conn = $emHelper->getEntityManager()->getConnection();
+
+ foreach ($sqls as $sql) {
+ $conn->executeQuery($sql);
+ }
+ $output->writeln(sprintf('Database schema updated successfully! "<info>%s</info>" queries were executed', count($sqls)));
+ } else {
+ $output->writeln('<comment>ATTENTION</comment>: This operation should not be executed in a production environment.');
+ $output->writeln(' Use the incremental update to detect changes during development and use');
+ $output->writeln(' the SQL DDL provided to manually update your database in production.');
+ $output->writeln('');
+
+ $output->writeln(sprintf('The Schema-Tool would execute <info>"%s"</info> queries to update the database.', count($sqls)));
+ $output->writeln('Please run the operation by passing one of the following options:');
+
+ $output->writeln(sprintf(' <info>%s --force</info> to execute the command', $this->getName()));
+ $output->writeln(sprintf(' <info>%s --dump-sql</info> to dump the SQL statements to the screen', $this->getName()));
+ }
+ }
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Listener/WikiTagDocumentListener.php Fri Nov 04 15:59:49 2011 +0100
@@ -0,0 +1,177 @@
+<?php
+/*
+ * This file is part of the WikiTagBundle package.
+ *
+ * (c) IRI <http://www.iri.centrepompidou.fr/>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace IRI\Bundle\WikiTagBundle\Listener;
+
+use Doctrine\DBAL\Schema\Table;
+
+use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs;
+use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
+use Doctrine\ORM\Tools\ToolEvents;
+use Doctrine\Common\EventSubscriber;
+use Doctrine\ORM\Events;
+use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+
+/**
+ * Doctrine ORM listener updating the document index
+ *
+ * @author ymh
+ *
+ */
+class WikiTagDocumentListener implements EventSubscriber
+{
+
+ /**
+ * @var ContainerInterface
+ */
+ private $container;
+
+ /**
+ * Constructor
+ *
+ * @param ContainerInterface $container
+ */
+ public function __construct(ContainerInterface $container)
+ {
+ $this->container = $container;
+ }
+
+ public function getContainer()
+ {
+ return $this->container;
+ }
+
+ public function getSubscribedEvents()
+ {
+ return array(
+ Events::loadClassMetadata,
+ ToolEvents::postGenerateSchemaTable,
+ );
+ }
+
+
+ /**
+ *
+ * Enter description here ...
+ * @param LoadClassMetadataEventArgs $args
+ */
+ public function loadClassMetadata(LoadClassMetadataEventArgs $args)
+ {
+ //check that IRI\\Bundle\\WikiTagBundle\\Entity\\Document exists. if not create it and load it
+ //
+ $path = $this->container->getParameter('kernel.cache_dir')."/wikitag";
+ $file = "$path/IRI/Bundle/WikiTagBundle/Entity/Document.php";
+ $config_file = $this->container->getParameter('kernel.root_dir')."/app/config/config.yml";
+
+ if(file_exists($file) && file_exists($config_file) && (filemtime($file)<filemtime($config_file)))
+ {
+ unlink($file);
+ }
+ if(!class_exists("\IRI\Bundle\WikiTagBundle\Entity\Document"))
+ {
+ $schema_utils = $this->getContainer()->get("wikitag.shema_utils");
+ $classCode = $schema_utils->generateDocumentClass();
+
+ $logger = $this->container->get('logger');
+ $logger->debug("File to generate : $file");
+ if(!file_exists(dirname($file)) && !mkdir(dirname($file),0777,true))
+ {
+ throw new Exception("Impossible to create document file");
+ }
+ file_put_contents($file, $classCode);
+
+ $document_schema = $args->getEntityManager()->getClassMetadata("IRI\\Bundle\\WikiTagBundle\\Entity\\Document");
+ return;
+ }
+
+
+ $metadata = $args->getClassMetadata();
+ if($metadata->name === "IRI\\Bundle\\WikiTagBundle\\Entity\\Document")
+ {
+ $document_class = $this->container->getParameter('wiki_tag.document_class');
+
+ $logger = $this->container->get('logger');
+ $logger->debug("DocumentListener: Add ExternalId Mapping");
+
+ $document_id_column = $this->container->getParameter('wiki_tag.document_id_column');
+
+ $logger->debug("DocumentListener: external id def : " . print_r($document_id_column, true));
+
+ /*$target_metadata = $args->getEntityManager()->getClassMetadata($document_class);
+ $mapping = array_replace(array(), $target_metadata->getFieldMapping($document_id_column));
+ $mapping['fieldName'] = 'externalId';
+ $mapping['columnName'] = 'external_id';
+ $mapping['id'] = false;
+ $metadata->mapField($mapping);*/
+ $metadata->mapOneToOne(array(
+ 'targetEntity' => $document_class,
+ 'fieldName' => 'externalId',
+ 'joinColumns' => array(0=>array(
+ 'name' => 'external_id',
+ 'referencedColumnName' => $document_id_column
+ )),
+ ));
+
+ //map the fields
+ $fields = $this->container->getParameter('wiki_tag.fields');
+
+ $def_columns = array();
+ foreach ( $fields as $name => $field_def)
+ {
+ if(isset($field_def['type']))
+ {
+ $type = $field_def['type'];
+ }
+ if(!isset($type) || is_null($type) || strlen($type) == 0)
+ {
+ $type = "text";
+ }
+ $mapping = array('fieldName' => $name, 'type' => $type);
+ if($type == 'string')
+ {
+ if(isset($field_def['length']))
+ {
+ $length = $field_def['length'];
+ }
+ if(!isset($length))
+ {
+ $length = 1024;
+ }
+ elseif (!is_int($length))
+ {
+ $length = intval($length);
+ }
+ $mapping['length'] = $length;
+ }
+ $metadata->mapField($mapping);
+ $def_columns[] = $name;
+ $metadata->table['indexes']["${name}_document_fulltext_idx"] = array( 'columns' => array("$name",));
+ }
+ $def_columns[] = "tags_str";
+ $metadata->table['indexes']["all_document_fulltext_idx"] = array('columns'=> $def_columns);
+ }
+ }
+
+ public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $args)
+ {
+
+ if($args->getClassMetadata()->name === "IRI\\Bundle\\WikiTagBundle\\Entity\\Document")
+ {
+ $logger = $this->container->get('logger');
+ $logger->debug("Generate schema table ".$args->getClassTable()->getName());
+
+ $args->getClassTable()->addOption('engine','MyISAM');
+ }
+ }
+
+
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Search/Search.php Fri Nov 04 15:59:49 2011 +0100
@@ -0,0 +1,29 @@
+<?php
+/*
+ * This file is part of the WikiTagBundle package.
+ *
+ * (c) IRI <http://www.iri.centrepompidou.fr/>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace IRI\Bundle\WikiTagBundle\Search;
+
+use Symfony\Component\DependencyInjection\ContainerAware;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class Search extends ContainerAware
+{
+
+ public function getContainer()
+ {
+ return $this->container;
+ }
+
+ public function __construct(ContainerInterface $container)
+ {
+ $this->setContainer($container);
+ }
+
+
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Utils/SchemaUtils.php Fri Nov 04 15:59:49 2011 +0100
@@ -0,0 +1,167 @@
+<?php
+/*
+ * This file is part of the WikiTagBundle package.
+ *
+ * (c) IRI <http://www.iri.centrepompidou.fr/>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace IRI\Bundle\WikiTagBundle\Utils;
+
+use Mandango\Mondator\Definition\Definition;
+use Mandango\Mondator\Definition\Property;
+use Mandango\Mondator\Definition\Method;
+use Mandango\Mondator\Dumper;
+
+class SchemaUtils
+{
+ /**
+ * The container for the service
+ * @var unknown_type
+ */
+ protected $container;
+
+ /**
+ * Accessor for the container property.
+ */
+ public function getContainer()
+ {
+ return $this->container;
+ }
+
+ /**
+ *
+ * construct the shema utils service injects the container
+ * @param unknown_type $container
+ */
+ public function __construct($container)
+ {
+ $this->container = $container;
+ }
+
+
+
+ /**
+ * Return the sql to create the document table full text indexes
+ * @return array
+ */
+ public function createFullTextIndexes()
+ {
+ $sql_code = array();
+ $fields = $this->getContainer()->getParameter('wiki_tag.fields');
+ $def_columns = array();
+ foreach ( $fields as $name => $field_def)
+ {
+ if(isset($field_def['type']))
+ {
+ $type = $field_def['type'];
+ }
+ if(!isset($type) || is_null($type) || strlen($type) == 0)
+ {
+ $type = "text";
+ }
+
+ if($type === 'text')
+ {
+ $def_column = "$name(4096)";
+ }
+ else
+ {
+ $def_column = $name;
+ }
+ $def_columns[] = $def_column;
+
+ $sql_code[] = "ALTER IGNORE TABLE wikitag_document DROP INDEX ${name}_document_fulltext_idx";
+ $sql_code[] = "ALTER TABLE wikitag_document ADD FULLTEXT INDEX ${name}_document_fulltext_idx ($def_column)";
+ }
+
+ $sql_code[] = "ALTER IGNORE TABLE wikitag_document DROP INDEX tags_str_document_fulltext_idx";
+ $sql_code[] = "ALTER TABLE wikitag_document ADD FULLTEXT INDEX tags_str_document_fulltext_idx (tags_str)";
+
+ $sql_code[] = "ALTER IGNORE TABLE wikitag_document DROP INDEX all_document_fulltext_idx";
+ $sql_code[] = "ALTER TABLE wikitag_document ADD FULLTEXT INDEX all_document_fulltext_idx (".join(",", $def_columns).")";
+
+ return $sql_code;
+
+ }
+
+ public function filter_foreign_key(array $sqls)
+ {
+ $res_sqls = array();
+ //TODO : take the column and table names from the schema
+ foreach ($sqls as $sql) {
+ if(!preg_match("/ADD CONSTRAINT .+ FOREIGN KEY \(.*\) REFERENCES wikitag_document\(id\)/i", $sql))
+ {
+ $res_sqls[] = $sql;
+ }
+ }
+
+ return $res_sqls;
+
+ }
+
+ public function filter_index_creation(array $sqls)
+ {
+ $res_sqls = array();
+
+ $replace_regexps = array();
+
+ $fields = $this->getContainer()->getParameter('wiki_tag.fields');
+ $field_names = array();
+ foreach ( $fields as $name => $field_def)
+ {
+ // create regular expression
+ $replace_regexps[] = "/INDEX (${name}_document_fulltext_idx (?:ON wikitag_document ){0,1}\(${name}\))/";
+ $field_names[] = " ?${name},?";
+ }
+ $field_names[] = " ?tags_str,?";
+ $replace_regexps[] = "/INDEX (tags_str_document_fulltext_idx (?:ON wikitag_document ){0,1}\(tags_str\))/";
+ $replace_regexps[] = "/INDEX (all_document_fulltext_idx (?:ON wikitag_document ){0,1}\((?:".implode("|",$field_names)."){".count($field_names)."}\))/";
+
+ foreach($sqls as $sql)
+ {
+ if(strrpos($sql,"wikitag_document"))
+ {
+ $sql = preg_replace($replace_regexps, "FULLTEXT INDEX $1", $sql);
+ }
+ $res_sqls[] = $sql;
+ }
+
+ return $res_sqls;
+ }
+
+ public function generateDocumentClass()
+ {
+ $definition = new Definition('IRI\Bundle\WikiTagBundle\Entity\Document');
+
+ $definition->setParentClass('\IRI\Bundle\WikiTagBundle\Model\Document');
+
+ $fields = $this->getContainer()->getParameter('wiki_tag.fields');
+
+ foreach ( $fields as $name => $field_def)
+ {
+ $property = new Property("private", $name, NULL);
+ $definition->addProperty($property);
+
+ $get_method = new Method("public", "get".ucfirst($name), NULL, <<<EOF
+ return \$this->$name;
+EOF
+ );
+ $definition->addMethod($get_method);
+
+ $set_method = new Method("public", "set".ucfirst($name), "\$$name", <<<EOF
+ \$this->$name = \$$name;
+EOF
+ );
+ $definition->addMethod($set_method);
+
+ }
+
+ $dumper = new Dumper($definition);
+ $classCode = $dumper->dump();
+
+ return $classCode;
+ }
+}
\ No newline at end of file