diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..1eb1ebe --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +install: +- curl -sS https://getcomposer.org/installer | php +before_script: +- php composer.phar update -n +script: +- vendor/bin/phpcs . --ignore=*/vendor/* --standard=PSR2 --extensions=php +- vendor/bin/phpunit -c phpunit.xml diff --git a/Command/IndexerQueueCommand.php b/Command/IndexerQueueCommand.php index 8bc8f7a..92952ec 100644 --- a/Command/IndexerQueueCommand.php +++ b/Command/IndexerQueueCommand.php @@ -14,11 +14,12 @@ use DateTime; use DateTimeZone; -use Integrated\Common\Content\ContentInterface; use Integrated\Common\ContentType\ResolverInterface; use Integrated\Common\Solr\Indexer\Job; use Integrated\Common\Queue\QueueInterface; +use Integrated\Bundle\ContentBundle\Document\Content\Content; + use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Helper\ProgressHelper; @@ -175,32 +176,21 @@ protected function executeDelete(InputInterface $input, OutputInterface $output) */ protected function executeIndex(InputInterface $input, OutputInterface $output) { - $result = null; + // Don't hydrate for performance reasons + $builder = $this->getDocumentManager()->createQueryBuilder(Content::class); + $builder->select('id', 'contentType', 'class')->hydrate(false); if ($input->getOption('full')) { - - //use createQueryBuilder for performance reasons - $qb = $this->getDocumentManager()->createQueryBuilder( - 'Integrated\Bundle\ContentBundle\Document\Content\Content' - ) - ->select('id', 'contentType', 'class'); - $result = $qb->getQuery()->execute(); + $result = $builder->getQuery()->execute(); // The entire site is going to be reindex so everything that is now in the queue // will be redone so just clear it so content is not double indexed. $this->getQueue()->clear(); } else { - $criteria['$or'] = []; + $builder->field('contentType')->in($input->getArgument('id')); - foreach ($input->getArgument('id') as $id) { - $criteria['$or'][] = ['contentType' => $id]; - } - - $result = $this->getDocumentManager() - ->getUnitOfWork() - ->getDocumentPersister('Integrated\Bundle\ContentBundle\Document\Content\Content') - ->loadAll($criteria); + $result = $builder->getQuery()->execute(); } if ($count = $result->count()) { @@ -251,17 +241,17 @@ protected function doIndex(Cursor $cursor, ProgressHelper $progress) $count = 0; $manager = $this->getDocumentManager(); - /** @var ContentInterface $document */ - foreach ($cursor as $document) { $progress->advance(); $job = new Job('ADD'); - $job->setOption('document.id', $document->getContentType() . '-' . $document->getId()); + $contentType = isset($document['contentType']) ? $document['contentType'] : ''; + + $job->setOption('document.id', $contentType . '-' . $document['_id']); - $job->setOption('document.data', $this->getSerializer()->serialize($document, 'json')); - $job->setOption('document.class', get_class($document)); + $job->setOption('document.data', json_encode(['id' => $document['_id']])); + $job->setOption('document.class', $document['class']); $job->setOption('document.format', 'json'); $queue->push($job); diff --git a/Command/IndexerRunCommand.php b/Command/IndexerRunCommand.php index 9cb1fae..bd70b90 100644 --- a/Command/IndexerRunCommand.php +++ b/Command/IndexerRunCommand.php @@ -11,22 +11,72 @@ namespace Integrated\Bundle\SolrBundle\Command; -use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; +use Exception; + +use Integrated\Bundle\SolrBundle\EventListener\DoctrineClearEventSubscriber; +use Integrated\Bundle\SolrBundle\Process\ArgumentProcess; +use Integrated\Bundle\SolrBundle\Process\ProcessPoolGenerator; + +use Integrated\Common\Queue\Provider\DBAL\QueueProvider; +use Integrated\Common\Solr\Indexer\Indexer; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Process\Process; use Symfony\Component\Filesystem\LockHandler; -use Exception; - -use Integrated\Common\Solr\Indexer\IndexerInterface; - /** * @author Jan Sanne Mulder */ -class IndexerRunCommand extends ContainerAwareCommand +class IndexerRunCommand extends Command { + /** + * @var Indexer + */ + protected $indexer; + + /** + * @var QueueProvider + */ + protected $queueProvider; + + /** + * @var string + */ + protected $workingDirectory; + + /** + * @var DoctrineClearEventSubscriber + */ + protected $clearEventSubscriber; + + /** + * @var KernelInterface + */ + protected $kernel; + + /** + * @param Indexer $indexer + * @param QueueProvider $queueProvider + * @param KernelInterface $kernel + * @param DoctrineClearEventSubscriber $clearEventSubscriber + * @param string $workingDirectory + */ + public function __construct(Indexer $indexer, QueueProvider $queueProvider, DoctrineClearEventSubscriber $clearEventSubscriber, KernelInterface $kernel, $workingDirectory) + { + parent::__construct(); + + $this->indexer = $indexer; + $this->queueProvider = $queueProvider; + $this->workingDirectory = $workingDirectory; + $this->clearEventSubscriber = $clearEventSubscriber; + $this->kernel = $kernel; + } + /** * @see Command */ @@ -48,7 +98,19 @@ protected function configure() 'Time in milliseconds to wait between runs (in combination with --full or --daemon)', 0 ) - ->setDescription('Execute a sol indexer run') + ->addArgument( + 'processes', + InputArgument::OPTIONAL, + 'Creates a number of proccess that run the queue', + 0 + ) + ->addOption( + 'blocking', + 'b', + InputOption::VALUE_NONE, + 'Block the current command until all sub-processes are done' + ) + ->setDescription('Execute a solr indexer run') ->setHelp(' The %command.name% command starts a indexer run. @@ -58,39 +120,44 @@ protected function configure() } /** - * @see Command::execute() + * @param InputInterface $input + * @param OutputInterface $output + * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { - if ($input->getOption('full') || $input->getOption('daemon')) { + if ($argument = $input->getArgument('processes')) { + return $this->runProcess(new ArgumentProcess($argument), $input, $output); + } else if ($input->getOption('full') || $input->getOption('daemon')) { return $this->runExternal($input, $output); } - return $this->runInternal($input, $output); + return $this->runInternal(self::class, $output); } /** - * @param InputInterface $input + * @param string $lock * @param OutputInterface $output * @return int */ - private function runInternal(InputInterface $input, OutputInterface $output) + private function runInternal($lock, OutputInterface $output) { try { - $lock = new LockHandler('Integrated\Bundle\SolrBundle\Command\IndexerRunCommand'); - $attemps = 0; + $lock = new LockHandler($lock); + $attempts = 0; while (!$lock->lock()) { - //retry for almost a minute, otherwise don't throw an error (after all another indexer is running) - if ($attemps++ >= 10) { + // Retry for almost a minute, otherwise don't throw an error (after all another indexer is running) + if ($attempts++ >= 10) { return 0; } sleep(5); } - /** @var IndexerInterface $indexer */ - $indexer = $this->getContainer()->get('integrated_solr.indexer'); - $indexer->execute(); + if ($output->isDebug() && method_exists($this->indexer, 'setDebug')) { + $this->indexer->setDebug(); + } + $this->indexer->execute(); } catch (Exception $e) { $output->writeln("Aborting: " . $e->getMessage()); @@ -107,24 +174,27 @@ private function runInternal(InputInterface $input, OutputInterface $output) */ private function runExternal(InputInterface $input, OutputInterface $output) { - $wait = (int)$input->getOption('wait'); + $wait = (int) $input->getOption('wait'); $wait = $wait * 1000; // convert from milli to micro while (true) { + // Run a external process $process = new Process( - 'php app/console solr:indexer:run -e ' . $input->getOption('env'), - $this->getRootDir() + sprintf('php app/console solr:indexer:run -e %s', $this->kernel->getEnvironment()), + $this->workingDirectory ); $process->setTimeout(0); - $process->run(); + $process->run(function ($type, $buffer) use ($output) { + $output->write($buffer, false, $type); + }); if (!$process->isSuccessful()) { break; // terminate when there is a error } if (!$input->getOption('daemon')) { - if (!$this->getContainer()->get('integrated_solr.indexer')->getQueue()->count()) { + if (!$this->indexer->getQueue()->count()) { break; } } @@ -136,10 +206,72 @@ private function runExternal(InputInterface $input, OutputInterface $output) } /** - * @return string + * @param ArgumentProcess $argument + * @param InputInterface $input + * @param OutputInterface $output + * @return int */ - protected function getRootDir() + private function runProcess(ArgumentProcess $argument, InputInterface $input, OutputInterface $output) { - return realpath($this->getContainer()->get('kernel')->getRootDir() . '/..'); + if ($argument->isParentProcess()) { + // Create pool generator to generate the processes + $generator = new ProcessPoolGenerator($input, $this->kernel); + $pool = $generator->getProcessesPool($argument, $this->workingDirectory); + + // Start them accordingly + foreach ($pool as $i => $process) { + // Run it + $process->start(); + + // Tell somebody + $output->writeln(sprintf('Started process %d with pid %d to run the queue', ($i+1), $process->getPid())); + } + + if ($input->getOption('blocking')) { + $output->writeln('Running in blocking mode, waiting until all started processes are done'); + + // While the pool contains processes we're running + while ($pool->count()) { + foreach ($pool as $i => $process) { + // Read stout for anything to pass thru + if ($processOutput = $process->getIncrementalOutput()) { + $output->writeln(sprintf('Prcocess %d: %s', $i, $processOutput)); + } + // Read sterr for anything to pass thru + if ($processOutput = $process->getIncrementalErrorOutput()) { + $output->writeln(sprintf('Prcocess %d: %s', $i, $processOutput)); + } + + if (!$process->isRunning()) { + // Tell the user + $output->writeln(sprintf('Process %d finished', ($i+1))); + + // This one is important + $pool->removeElement($process); + } + } + + // Don't create a cpu load, check periodically + sleep(1); + } + } + } else { + // Set the modulo to run over the data set with x processes, creating a unique list per thread + $this->queueProvider->setOption('where', sprintf('(id %% %d) = %d', $argument->getProcessMax(), $argument->getProcessNumber())); + + // Add the clear event listener only for the thread + $this->indexer->getEventDispatcher()->addSubscriber($this->clearEventSubscriber); + + // Seems to be a sub-process, ran it with a the number appended to the class + while ($this->indexer->getQueue()->count()) { + $this->runInternal(sprintf('%s:%d', self::class, $argument->getProcessNumber()), $output); + + // Give them cores some relaxation + usleep(5000); + } + } + + // Good to go + return 0; } } diff --git a/Command/WorkerCommand.php b/Command/WorkerCommand.php new file mode 100644 index 0000000..3a6600c --- /dev/null +++ b/Command/WorkerCommand.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Integrated\Bundle\SolrBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Filesystem\LockHandler; + +/** + * @author Jan Sanne Mulder + */ +class WorkerCommand extends ContainerAwareCommand +{ + /** + * @see Command + */ + protected function configure() + { + $this + ->setName('solr:worker:run') + + ->addOption('tasks', 't', InputOption::VALUE_REQUIRED, 'The maximum number of tasks to execute in one worker run', null) + + ->setDescription('Execute worker task from the queue.') + ->setHelp(' +The %command.name% command starts a solr worker run. + +php %command.full_name% +'); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $lock = new LockHandler(self::class); + + if (!$lock->lock()) { + return; + } + + try { + $worker = $this->getContainer()->get('integrated_solr.worker'); + + if (null !== ($tasks = $input->getOption('tasks'))) { + $worker->setOption('tasks', intval($tasks)); + } + + $worker->execute(); + } finally { + $lock->release(); + } + } +} diff --git a/DataCollector/SolariumDataCollector.php b/DataCollector/SolariumDataCollector.php index 342fc29..ebb4fc1 100644 --- a/DataCollector/SolariumDataCollector.php +++ b/DataCollector/SolariumDataCollector.php @@ -30,7 +30,7 @@ class SolariumDataCollector extends Plugin implements DataCollectorInterface, \S /** * @var array */ - protected $data = array(); + protected $data = []; /** * @var float @@ -38,17 +38,17 @@ class SolariumDataCollector extends Plugin implements DataCollectorInterface, \S protected $startTime; /** - * @inheritdoc + * {@inheritdoc} */ protected function initPluginType() { $dispatcher = $this->client->getEventDispatcher(); - $dispatcher->addListener(Events::PRE_EXECUTE_REQUEST, array($this, 'preExecuteRequest'), 1000); - $dispatcher->addListener(Events::POST_EXECUTE_REQUEST, array($this, 'postExecuteRequest'), -1000); + $dispatcher->addListener(Events::PRE_EXECUTE_REQUEST, [$this, 'preExecuteRequest'], 1000); + $dispatcher->addListener(Events::POST_EXECUTE_REQUEST, [$this, 'postExecuteRequest'], -1000); } /** - * @inheritdoc + * {@inheritdoc} */ public function collect(Request $request, Response $response, \Exception $exception = null) { @@ -74,12 +74,12 @@ public function preExecuteRequest(PreExecuteRequest $event) */ public function postExecuteRequest(PostExecuteRequest $event) { - $this->data['queries'][] = array( + $this->data['queries'][] = [ 'request' => $event->getRequest(), 'response' => $event->getResponse(), 'duration' => microtime(true) - $this->startTime, 'base_uri' => $event->getEndpoint()->getBaseUri(), - ); + ]; } /** @@ -87,7 +87,7 @@ public function postExecuteRequest(PostExecuteRequest $event) */ public function getQueries() { - return isset($this->data['queries']) ? $this->data['queries'] : array(); + return isset($this->data['queries']) ? $this->data['queries'] : []; } /** @@ -115,7 +115,7 @@ public function getName() } /** - * @inheritdoc + * {@inheritdoc} */ public function serialize() { @@ -123,7 +123,7 @@ public function serialize() } /** - * @inheritdoc + * {@inheritdoc} */ public function unserialize($data) { diff --git a/Decorator/ContentProviderDetachDecorator.php b/Decorator/ContentProviderDetachDecorator.php new file mode 100644 index 0000000..6439024 --- /dev/null +++ b/Decorator/ContentProviderDetachDecorator.php @@ -0,0 +1,50 @@ + +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +namespace Integrated\Bundle\SolrBundle\Decorator; + +use Doctrine\Common\Persistence\ObjectManager; +use Integrated\Common\Solr\Task\Provider\ContentProviderInterface; +use Integrated\Bundle\SolrBundle\Iterator\DetachIterator; + +/** + * @author Patrick Mestebeld + */ +class ContentProviderDetachDecorator implements ContentProviderInterface +{ + /** + * @var ContentProviderInterface + */ + private $provider; + + /** + * @var ObjectManager + */ + private $manager; + + /** + * @param ContentProviderInterface $provider + * @param ObjectManager $manager + */ + public function __construct(ContentProviderInterface $provider, ObjectManager $manager) + { + $this->provider = $provider; + $this->manager = $manager; + } + + /** + * {@inheritdoc} + */ + public function getReferenced($id) + { + return new DetachIterator($this->provider->getReferenced($id), $this->manager); + } +} diff --git a/Decorator/ContentTypeProviderDetachDecorator.php b/Decorator/ContentTypeProviderDetachDecorator.php new file mode 100644 index 0000000..13ad9f9 --- /dev/null +++ b/Decorator/ContentTypeProviderDetachDecorator.php @@ -0,0 +1,50 @@ + +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +namespace Integrated\Bundle\SolrBundle\Decorator; + +use Doctrine\Common\Persistence\ObjectManager; +use Integrated\Common\Solr\Task\Provider\ContentTypeProviderInterface; +use Integrated\Bundle\SolrBundle\Iterator\DetachIterator; + +/** + * @author Patrick Mestebeld + */ +class ContentTypeProviderDetachDecorator implements ContentTypeProviderInterface +{ + /** + * @var ContentTypeProviderInterface + */ + private $provider; + + /** + * @var ObjectManager + */ + private $manager; + + /** + * @param ContentTypeProviderInterface $provider + * @param ObjectManager $manager + */ + public function __construct(ContentTypeProviderInterface $provider, ObjectManager $manager) + { + $this->provider = $provider; + $this->manager = $manager; + } + + /** + * {@inheritdoc} + */ + public function getContent($id) + { + return new DetachIterator($this->provider->getContent($id), $this->manager); + } +} diff --git a/DependencyInjection/CompilerPass/RegisterConfigFileProviderPass.php b/DependencyInjection/CompilerPass/RegisterConfigFileProviderPass.php index d87af92..363143c 100644 --- a/DependencyInjection/CompilerPass/RegisterConfigFileProviderPass.php +++ b/DependencyInjection/CompilerPass/RegisterConfigFileProviderPass.php @@ -11,14 +11,16 @@ namespace Integrated\Bundle\SolrBundle\DependencyInjection\CompilerPass; +use Integrated\Common\Converter\Config\Provider\XmlProvider; + use ReflectionClass; use Symfony\Component\Config\Resource\FileResource; - use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Finder\Finder; /** * @author Jan Sanne Mulder @@ -72,7 +74,7 @@ protected function addProvider(ContainerBuilder $container, $dir, $bundle) return null; } - $definition = new Definition('%integrated_solr.converter.config.provider.file.class%'); + $definition = new Definition(XmlProvider::class); $definition->setPublic(false); $definition->setArguments([$this->addFinder($container, $dir . '/Resources/config/solr', $bundle)]); @@ -103,7 +105,7 @@ protected function addFinder(ContainerBuilder $container, $dir, $bundle) $container->addResource(new FileResource($dir)); // not really sure what this does - $definition = new Definition('%integrated_solr.converter.finder.class%'); + $definition = new Definition(Finder::class); $definition->setPublic(false); $definition->addMethodCall('in', [$dir]); diff --git a/DependencyInjection/CompilerPass/RegisterTaskHandlerPass.php b/DependencyInjection/CompilerPass/RegisterTaskHandlerPass.php new file mode 100644 index 0000000..0bd3eee --- /dev/null +++ b/DependencyInjection/CompilerPass/RegisterTaskHandlerPass.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Integrated\Bundle\SolrBundle\DependencyInjection\CompilerPass; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Jan Sanne Mulder + */ +class RegisterTaskHandlerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('integrated_solr.worker.handler.registry_builder')) { + return; + } + + $definition = $container->getDefinition('integrated_solr.worker.handler.registry_builder'); + + foreach ($container->findTaggedServiceIds('integrated_solr.task') as $service => $tags) { + foreach ($tags as $attributes) { + $definition->addMethodCall('addHandler', [$attributes['class'], new Reference($service)]); + } + } + } +} diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 305099a..009b261 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -32,8 +32,11 @@ public function getConfigTreeBuilder() ->arrayNode('endpoints') ->prototype('array') ->children() + ->scalarNode('scheme')->defaultValue('http')->end() ->scalarNode('host')->defaultValue('localhost')->end() ->scalarNode('port')->defaultValue(8983)->end() + ->scalarNode('username')->defaultValue(null)->end() + ->scalarNode('password')->defaultValue(null)->end() ->scalarNode('path')->defaultValue('/solr')->end() ->scalarNode('core')->end() ->scalarNode('timeout')->defaultValue(5)->end() diff --git a/DependencyInjection/IntegratedSolrExtension.php b/DependencyInjection/IntegratedSolrExtension.php index 0df92e7..122e2a5 100644 --- a/DependencyInjection/IntegratedSolrExtension.php +++ b/DependencyInjection/IntegratedSolrExtension.php @@ -11,13 +11,14 @@ namespace Integrated\Bundle\SolrBundle\DependencyInjection; -use Symfony\Component\Config\FileLocator; +use Solarium\Client; +use Solarium\Core\Client\Endpoint; +use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; - use Symfony\Component\HttpKernel\DependencyInjection\Extension; /** @@ -38,10 +39,15 @@ public function load(array $configs, ContainerBuilder $container) $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('converter.xml'); + $loader->load('event.xml'); + $loader->load('event_listeners.xml'); + $loader->load('command.xml'); $loader->load('indexer.xml'); $loader->load('queue.xml'); $loader->load('solarium.xml'); + $loader->load('task.xml'); $loader->load('types.xml'); + $loader->load('worker.xml'); if ($container->getParameter('kernel.debug')) { $loader->load('collector.xml'); @@ -50,13 +56,13 @@ public function load(array $configs, ContainerBuilder $container) $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); - $endpoints = array(); + $endpoints = []; foreach ($config['endpoints'] as $name => $options) { $options['key'] = $name; $container->setDefinition( 'solarium.client.endpoint.' . $name, - new Definition('%integrated_solr.solarium.endpoint.class%', array($options)) + new Definition(Endpoint::class, [$options]) ); $endpoints[] = new Reference('solarium.client.endpoint.' . $name); @@ -64,13 +70,19 @@ public function load(array $configs, ContainerBuilder $container) $container->setDefinition( 'solarium.client', - new Definition('%integrated_solr.solarium.client.class%', array(array('endpoint' => $endpoints))) + new Definition( + Client::class, + [ + ['endpoint' => $endpoints], + new Reference('integrated_solr.event.dispatcher') + ] + ) ); if ($container->getParameter('kernel.debug')) { $container->getDefinition('solarium.client')->addMethodCall( 'registerPlugin', - array('solarium.client.logger', new Reference('integrated_solr.solarium.data_collector')) + ['solarium.client.logger', new Reference('integrated_solr.solarium.data_collector')] ); } } diff --git a/EventListener/DoctrineClearEventSubscriber.php b/EventListener/DoctrineClearEventSubscriber.php new file mode 100644 index 0000000..d7b035e --- /dev/null +++ b/EventListener/DoctrineClearEventSubscriber.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Integrated\Bundle\SolrBundle\EventListener; + +use Doctrine\ODM\MongoDB\DocumentManager; +use Doctrine\ORM\EntityManager; + +use Integrated\Common\Solr\Indexer\Event\MessageEvent; +use Integrated\Common\Solr\Indexer\Events; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @author Johnny Borg + */ +class DoctrineClearEventSubscriber implements EventSubscriberInterface +{ + /** + * @var DocumentManager + */ + private $documentManager; + + /** + * @var EntityManager + */ + private $entityManager; + + /** + * @param DocumentManager $documentManager + * @param EntityManager $entityManager + */ + public function __construct(DocumentManager $documentManager, EntityManager $entityManager) + { + $this->documentManager = $documentManager; + $this->entityManager = $entityManager; + } + + /** + * @return array + */ + public static function getSubscribedEvents() + { + return [ + Events::PROCESSED => 'processedEvent' + ]; + } + + /** + * @param MessageEvent $messageEvent + */ + public function processedEvent(MessageEvent $messageEvent) + { + $this->documentManager->clear(); + $this->entityManager->clear(); + } +} diff --git a/EventListener/IndexerErrorLogger.php b/EventListener/IndexerErrorLogger.php new file mode 100644 index 0000000..bfcb29b --- /dev/null +++ b/EventListener/IndexerErrorLogger.php @@ -0,0 +1,68 @@ + +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +namespace Integrated\Bundle\SolrBundle\EventListener; + +use Psr\Log\LoggerInterface; + +use Integrated\Common\Solr\Indexer\Event\ErrorEvent; +use Integrated\Common\Solr\Indexer\Events; +use Integrated\Common\Solr\Indexer\Job; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @author Jan Sanne Mulder + */ +class IndexerErrorLogger implements EventSubscriberInterface +{ + /** + * @var LoggerInterface + */ + private $logger = null; + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return [ + Events::ERROR => 'onError' + ]; + } + + /** + * @param ErrorEvent $event + */ + public function onError(ErrorEvent $event) + { + if (null === $this->logger) { + return; + } + + $payload = $event->getMessage()->getPayload(); + + if ($payload instanceof Job) { + $this->logger->error($event->getException()->getMessage(), [ + 'action' => $payload->getAction(), + 'options' => $payload->getOptions() + ]); + } + } + + /** + * @param LoggerInterface $logger + */ + public function setLogger(LoggerInterface $logger = null) + { + $this->logger = $logger; + } +} diff --git a/EventListener/WorkerErrorLogger.php b/EventListener/WorkerErrorLogger.php new file mode 100644 index 0000000..8b85d92 --- /dev/null +++ b/EventListener/WorkerErrorLogger.php @@ -0,0 +1,62 @@ + +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +namespace Integrated\Bundle\SolrBundle\EventListener; + +use Psr\Log\LoggerInterface; + +use Integrated\Common\Solr\Task\Event\ErrorEvent; +use Integrated\Common\Solr\Task\Events; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @author Jan Sanne Mulder + */ +class WorkerErrorLogger implements EventSubscriberInterface +{ + /** + * @var LoggerInterface + */ + private $logger = null; + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return [ + Events::ERROR => 'onError' + ]; + } + + /** + * @param ErrorEvent $event + */ + public function onError(ErrorEvent $event) + { + if (null === $this->logger) { + return; + } + + $this->logger->error($event->getException()->getMessage(), [ + 'payload' => serialize($event->getMessage()->getPayload()) + ]); + } + + /** + * @param LoggerInterface $logger + */ + public function setLogger(LoggerInterface $logger = null) + { + $this->logger = $logger; + } +} diff --git a/IntegratedSolrBundle.php b/IntegratedSolrBundle.php index a42a3a8..4b54987 100644 --- a/IntegratedSolrBundle.php +++ b/IntegratedSolrBundle.php @@ -12,9 +12,11 @@ namespace Integrated\Bundle\SolrBundle; use Integrated\Bundle\SolrBundle\DependencyInjection\CompilerPass\RegisterConfigFileProviderPass; +use Integrated\Bundle\SolrBundle\DependencyInjection\CompilerPass\RegisterTaskHandlerPass; use Integrated\Bundle\SolrBundle\DependencyInjection\CompilerPass\RegisterTypePass; use Integrated\Bundle\SolrBundle\DependencyInjection\IntegratedSolrExtension; +use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -24,7 +26,7 @@ class IntegratedSolrBundle extends Bundle { /** - * @inheritdoc + * {@inheritdoc} */ public function build(ContainerBuilder $container) { @@ -32,10 +34,17 @@ public function build(ContainerBuilder $container) $container->addCompilerPass(new RegisterConfigFileProviderPass()); $container->addCompilerPass(new RegisterTypePass()); + $container->addCompilerPass(new RegisterTaskHandlerPass()); + + $container->addCompilerPass(new RegisterListenersPass( + 'integrated_solr.event.dispatcher', + 'integrated_solr.event_listener', + 'integrated_solr.event_subscriber' + )); } /** - * @inheritdoc + * {@inheritdoc} */ public function getContainerExtension() { diff --git a/Iterator/DetachIterator.php b/Iterator/DetachIterator.php new file mode 100644 index 0000000..5fff24a --- /dev/null +++ b/Iterator/DetachIterator.php @@ -0,0 +1,82 @@ + +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +namespace Integrated\Bundle\SolrBundle\Iterator; + +use Doctrine\Common\Persistence\ObjectManager; +use Iterator; + +/** + * @author Patrick Mestebeld + */ +class DetachIterator implements Iterator +{ + /** + * @var ObjectManager + */ + private $manager; + + /** + * @var Iterator + */ + private $iterator; + + /** + * @param Iterator $iterator + * @param ObjectManager $manager + */ + public function __construct(Iterator $iterator, ObjectManager $manager) + { + $this->iterator = $iterator; + $this->manager = $manager; + } + + /** + * {@inheritdoc} + */ + public function current() + { + $this->manager->detach($current = $this->iterator->current()); + return $current; + } + + /** + * {@inheritdoc} + */ + public function next() + { + $this->iterator->next(); + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->iterator->key(); + } + + /** + * {@inheritdoc} + */ + public function valid() + { + return $this->iterator->valid(); + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + $this->iterator->rewind(); + } +} diff --git a/Process/ArgumentProcess.php b/Process/ArgumentProcess.php new file mode 100644 index 0000000..1c69fb0 --- /dev/null +++ b/Process/ArgumentProcess.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Integrated\Bundle\SolrBundle\Process; + +use Integrated\Bundle\SolrBundle\Process\Exception\FormatException; +use Integrated\Bundle\SolrBundle\Process\Exception\LogicException; + +/** + * @author Johnny Borg + */ +class ArgumentProcess +{ + /** + * @const string + */ + const FORMAT = '/^(\d):(\d)$/'; + + /** + * @var string + */ + protected $argument; + + /** + * @param string $argument + */ + public function __construct($argument) + { + $this->argument = $argument; + } + + /** + * @return bool + */ + public function isParentProcess() + { + return (false === strpos($this->argument, ':')); + } + + /** + * @return int + */ + public function getProcessNumber() + { + if (!$this->isParentProcess()) { + if (preg_match(self::FORMAT, $this->argument, $matches)) { + return $matches[1]; + } + + throw FormatException::noRegexMatch(); + } + + throw LogicException::invalidMethodCall(); + } + + /** + * @return int + * @throws FormatException + */ + public function getProcessMax() + { + if (!$this->isParentProcess()) { + if (preg_match(self::FORMAT, $this->argument, $matches)) { + return $matches[2]; + } + + throw FormatException::noRegexMatch(); + } + + return (int) $this->argument; + } +} diff --git a/Process/Exception/FormatException.php b/Process/Exception/FormatException.php new file mode 100644 index 0000000..4ce97fe --- /dev/null +++ b/Process/Exception/FormatException.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Integrated\Bundle\SolrBundle\Process\Exception; + +/** + * @author Johnny Borg + */ +class FormatException extends \Exception +{ + /** + * @return static + */ + public static function noRegexMatch() + { + return new static('Format does not required pattern'); + } +} diff --git a/Process/Exception/LogicException.php b/Process/Exception/LogicException.php new file mode 100644 index 0000000..a598245 --- /dev/null +++ b/Process/Exception/LogicException.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Integrated\Bundle\SolrBundle\Process\Exception; + +/** + * @author Johnny Borg + */ +class LogicException extends \Exception +{ + /** + * @return static + */ + public static function invalidMethodCall() + { + return new static('This method should not be called in this context'); + } + + /** + * @return static + */ + public static function noProcessesGenerated() + { + return new static('No processes could be generated with given input'); + } +} diff --git a/Process/ProcessPoolGenerator.php b/Process/ProcessPoolGenerator.php new file mode 100644 index 0000000..3d0e293 --- /dev/null +++ b/Process/ProcessPoolGenerator.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Integrated\Bundle\SolrBundle\Process; + +use Doctrine\Common\Collections\ArrayCollection; + +use Integrated\Bundle\SolrBundle\Process\Exception\LogicException; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Process\Process; + +/** + * @author Johnny Borg + */ +class ProcessPoolGenerator +{ + /** + * @const + */ + const COMMAND = 'php app/console %s %s %d:%d -e %s'; + + /** + * @var InputInterface + */ + private $input; + + /** + * @var Kernel + */ + private $kernel; + + /** + * @param InputInterface $input + * @param Kernel $kernel + */ + public function __construct(InputInterface $input, Kernel $kernel) + { + $this->input = $input; + $this->kernel = $kernel; + } + + /** + * @param ArgumentProcess $argumentProcess + * @param string $workingDirectory + * @return ArrayCollection|Process[] + * @throws LogicException + */ + public function getProcessesPool(ArgumentProcess $argumentProcess, $workingDirectory) + { + $result = new ArrayCollection(); + + for ($i = 0; $i < $argumentProcess->getProcessMax(); $i++) { + $result[] = new Process( + sprintf( + self::COMMAND, + $this->input->getFirstArgument(), + $this->input->getParameterOption('command'), + $i, + $argumentProcess->getProcessMax(), + $this->kernel->getEnvironment() + ), + $workingDirectory + ); + } + + if ($result->count()) { + return $result; + } + + throw LogicException::noProcessesGenerated(); + } +} diff --git a/Provider/MongoDBProvider.php b/Provider/MongoDBProvider.php new file mode 100644 index 0000000..f5c98c8 --- /dev/null +++ b/Provider/MongoDBProvider.php @@ -0,0 +1,71 @@ + +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +namespace Integrated\Bundle\SolrBundle\Provider; + +use Doctrine\ODM\MongoDB\DocumentRepository; +use Integrated\Common\Solr\Task\Provider\ContentProviderInterface; +use Integrated\Common\Solr\Task\Provider\ContentTypeProviderInterface; + +/** + * @author Jan Sanne Mulder + */ +class MongoDBProvider implements ContentProviderInterface, ContentTypeProviderInterface +{ + /** + * @var DocumentRepository + */ + private $repository; + + /** + * Constructor. + * + * @param DocumentRepository $repository + */ + public function __construct(DocumentRepository $repository) + { + $this->repository = $repository; + } + + /** + * {@inheritdoc} + */ + public function getReferenced($id) + { + $iterator = $this->repository->createQueryBuilder() + ->field('relations.references.$id')->equals($id) + ->getQuery() + ->getIterator(); + + if (method_exists($iterator, 'timeout')) { + $iterator->timeout(-1); + } + + return $iterator; + } + + /** + * {@inheritdoc} + */ + public function getContent($id) + { + $iterator = $this->repository->createQueryBuilder() + ->field('contentType')->equals($id) + ->getQuery() + ->getIterator(); + + if (method_exists($iterator, 'timeout')) { + $iterator->timeout(-1); + } + + return $iterator; + } +} diff --git a/README.md b/README.md index c839792..e4bc05b 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,13 @@ Adds support for Solr configuration and indexing of your documents or entities ## Documentation ## * [Integrated for developers website](http://www.integratedfordevelopers.com "Integrated for developers website") +### Commands ### +* The command **solr:indexer:queue** will queue up all the documents to be indexed +* The command **solr:indexer:run** will tranform the queued up documents to a solr compatible format and send them to solr for indexing. +* The command **solr:worker:run** will start a worker run and will process up to end of the queue or 1000 tasks, whichever comes first. + +It's recommended to the execute **solr:indexer:run** and **solr:worker:run** as automated tasks by configuring them as cronjobs. + ## Installation ## This bundle can be installed following these steps: @@ -26,6 +33,9 @@ This bundle can be installed following these steps: ); } +### Setup the queue ### +The solr bundle requires a queue to work properly, so if not already done setup the queue by executing the **init:queue** command. + ## License ## This bundle is under the MIT license. See the complete license in the bundle: diff --git a/Resources/config/collector.xml b/Resources/config/collector.xml index 831ce1c..fb7dd1f 100644 --- a/Resources/config/collector.xml +++ b/Resources/config/collector.xml @@ -4,15 +4,9 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - Integrated\Bundle\SolrBundle\DataCollector\SolariumDataCollector - - - - + diff --git a/Resources/config/command.xml b/Resources/config/command.xml new file mode 100644 index 0000000..65e51fd --- /dev/null +++ b/Resources/config/command.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + %kernel.root_dir%/.. + + + + + + + diff --git a/Resources/config/converter.xml b/Resources/config/converter.xml index 6aabb90..4f9e7ac 100644 --- a/Resources/config/converter.xml +++ b/Resources/config/converter.xml @@ -4,51 +4,30 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - Integrated\Common\Converter\Type\ResolvedTypeFactory - - Integrated\Common\Converter\Type\RegistryInterface - Integrated\Common\Converter\Type\RegistryBuilder - - Symfony\Component\Finder\Finder - - Integrated\Common\Converter\Config\Provider\ChainProvider - Integrated\Common\Converter\Config\Provider\XmlProvider - - Integrated\Common\Converter\Config\ConfigResolver - - Integrated\Common\Converter\ContainerFactory - - Integrated\Common\Converter\Converter - - - - + - + - + + + - + - + - + - + @@ -56,4 +35,4 @@ - \ No newline at end of file + diff --git a/Resources/config/event.xml b/Resources/config/event.xml new file mode 100644 index 0000000..ce4119c --- /dev/null +++ b/Resources/config/event.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/Resources/config/event_listeners.xml b/Resources/config/event_listeners.xml new file mode 100644 index 0000000..f056c90 --- /dev/null +++ b/Resources/config/event_listeners.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Resources/config/indexer.xml b/Resources/config/indexer.xml index 10819c4..d9318d5 100644 --- a/Resources/config/indexer.xml +++ b/Resources/config/indexer.xml @@ -4,28 +4,25 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - Integrated\MongoDB\Solr\Indexer\QueueSubscriber - - Symfony\Component\Serializer\Serializer - Integrated\MongoDB\Serializer\Normalizer\ContainerAwareDocumentNormalizer - Symfony\Component\Serializer\Encoder\JsonEncoder - - Integrated\Common\Solr\Indexer\Indexer + - + + + + - + + + - - + + - + @@ -34,25 +31,34 @@ - + doctrine.odm.mongodb.document_manager - + + + + + + + + + + json + - + + + - - - - - - - + + + + diff --git a/Resources/config/queue.xml b/Resources/config/queue.xml index bfc4394..9cc6dd5 100644 --- a/Resources/config/queue.xml +++ b/Resources/config/queue.xml @@ -4,44 +4,31 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - Integrated\Common\Queue\Provider\DBAL\Schema - Integrated\Common\Queue\Provider\DBAL\QueueProvider - Integrated\Common\Queue\QueueFactory - - Integrated\Common\Queue\Provider\Memory\QueueProvider - Integrated\Common\Queue\QueueFactory - - Integrated\Common\Queue\Queue - - - - + queue - + queue - + - + - + @@ -49,12 +36,20 @@ - - solr-indexer - + + + + solr-indexer + + + + + + solr-worker + + + + diff --git a/Resources/config/solarium.xml b/Resources/config/solarium.xml index 7397047..0ab4d27 100644 --- a/Resources/config/solarium.xml +++ b/Resources/config/solarium.xml @@ -4,13 +4,6 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - Solarium\Client - Solarium\Core\Client\Endpoint - - - diff --git a/Resources/config/task.xml b/Resources/config/task.xml new file mode 100644 index 0000000..ed500c3 --- /dev/null +++ b/Resources/config/task.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + Integrated\Bundle\ContentBundle\Document\Content\Content + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/config/types.xml b/Resources/config/types.xml index d2de6bc..b953a6b 100644 --- a/Resources/config/types.xml +++ b/Resources/config/types.xml @@ -4,43 +4,36 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - Integrated\Bundle\SolrBundle\Solr\Type\ClearType - Integrated\Bundle\SolrBundle\Solr\Type\CopyAppendType - Integrated\Bundle\SolrBundle\Solr\Type\CopyType - Integrated\Bundle\SolrBundle\Solr\Type\FieldAppendMapperType - Integrated\Bundle\SolrBundle\Solr\Type\FieldMapperType - Integrated\Bundle\SolrBundle\Solr\Type\RemoveType - - - - + + + + + - + - + - + - + - + - \ No newline at end of file + diff --git a/Resources/config/worker.xml b/Resources/config/worker.xml new file mode 100644 index 0000000..8143310 --- /dev/null +++ b/Resources/config/worker.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Solr/Type/FieldMapperType.php b/Solr/Type/FieldMapperType.php index 2769703..18f2f2f 100644 --- a/Solr/Type/FieldMapperType.php +++ b/Solr/Type/FieldMapperType.php @@ -39,7 +39,7 @@ class FieldMapperType implements TypeInterface /** * @var PropertyAccessorInterface */ - private $accessor; + protected $accessor; /** * Constructor. @@ -56,6 +56,29 @@ public function __construct(PropertyAccessorInterface $accessor = null) * {@inheritdoc} */ public function build(ContainerInterface $container, $data, array $options = []) + { + foreach ($this->groupFields($options) as $field => $config) { + $this->remove($container, $field); + + foreach ($this->read($data, $config) as $value) { + $this->append($container, $field, $value); + } + } + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'integrated.fields'; + } + + /** + * @param array $options + * @return array + */ + protected function groupFields(array $options = []) { $fields = []; @@ -74,21 +97,7 @@ public function build(ContainerInterface $container, $data, array $options = []) } } - foreach ($fields as $field => $config) { - $this->remove($container, $field); - - foreach ($this->read($data, $config) as $value) { - $this->append($container, $field, $value); - } - } - } - - /** - * {@inheritdoc} - */ - public function getName() - { - return 'integrated.fields'; + return $fields; } /** @@ -159,7 +168,6 @@ protected function readArray($data, array $paths, $separator = ' ') foreach ($paths as $index => $path) { if (is_array($path)) { - // Since $path is a array the $index with be treated as a path and the result of that // path is treated as a array. If the result is not a array then it will be placed in // a array to simulate that the result is a array. diff --git a/Solr/Type/JsonType.php b/Solr/Type/JsonType.php new file mode 100644 index 0000000..bce51f2 --- /dev/null +++ b/Solr/Type/JsonType.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Integrated\Bundle\SolrBundle\Solr\Type; + +use Symfony\Component\PropertyAccess\Exception\ExceptionInterface; + +use Integrated\Common\Converter\ContainerInterface; + +use Traversable; + +/** + * @author Ger Jan van den Bosch + */ +class JsonType extends FieldMapperType +{ + /** + * {@inheritdoc} + */ + public function build(ContainerInterface $container, $data, array $options = []) + { + foreach ($this->groupFields($options) as $field => $config) { + $this->remove($container, $field); + + if (is_array($config)) { + foreach ($this->readValues($data, $config) as $values) { + foreach ($values as $value) { + $this->append($container, $field, json_encode($value)); + } + } + } + } + } + + /** + * @param mixed $data + * @param array $paths + * + * @return array + */ + protected function readValues($data, array $paths) + { + $extracted = []; + + foreach ($paths as $index => $path) { + $index = (string)$index; + + if (is_array($path)) { + try { + $array = $this->accessor->getValue($data, $index); + + if (!is_array($array) && !$array instanceof Traversable) { + $array = [$array]; + } + } catch (ExceptionInterface $e) { + $array = []; + } + + $results = []; + + foreach ($array as $value) { + if ($path) { + $results[] = $this->readValues($value, $path); + } else { + if ($value = $this->convert($value)) { + $results[] = $value; + } + } + } + + $extracted[$index] = $results; + } else { + $extracted[$index] = $this->readString($data, $path); + } + } + + return $extracted; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'integrated.json'; + } +} diff --git a/Tests/Solr/Type/FieldMapperTypeTestTestObject.php b/Tests/Fixtures/TestObject.php similarity index 78% rename from Tests/Solr/Type/FieldMapperTypeTestTestObject.php rename to Tests/Fixtures/TestObject.php index 62f8679..39cebbc 100644 --- a/Tests/Solr/Type/FieldMapperTypeTestTestObject.php +++ b/Tests/Fixtures/TestObject.php @@ -9,16 +9,18 @@ * file that was distributed with this source code. */ -namespace Integrated\Bundle\SolrBundle\Tests\Solr\Type; +namespace Integrated\Bundle\SolrBundle\Tests\Fixtures; + +use ArrayObject; +use DateTime; /** - * Class FieldMapperTypeTestTestObject - * @package Integrated\Bundle\SolrBundle\Tests\Solr\Type + * @author Jan Sanne Mulder */ -class FieldMapperTypeTestTestObject +class TestObject { /** - * @var \DateTime + * @var DateTime */ public $datetime; @@ -57,7 +59,7 @@ class FieldMapperTypeTestTestObject protected $field4 = 'field4'; /** - * @var \ArrayObject + * @var ArrayObject */ public $arrayObject; @@ -66,25 +68,25 @@ class FieldMapperTypeTestTestObject */ public function __construct() { - $this->datetime = new \DateTime('2014-01-01 00:30 CET'); + $this->datetime = new DateTime('2014-01-01 00:30 CET'); - $this->arrayObject = new \ArrayObject([ + $this->arrayObject = new ArrayObject([ 'field1' => 'field1', 'field2' => 'field2', 'field3' => 'field3', - 'array1' => new \ArrayObject([ + 'array1' => new ArrayObject([ 'field1' => 'array1.1', 'field2' => 'array1.2', 'field3' => 'array1.3' - ], \ArrayObject::ARRAY_AS_PROPS), + ], ArrayObject::ARRAY_AS_PROPS), - 'array2' => new \ArrayObject([ + 'array2' => new ArrayObject([ 'field1' => 'array2.1', 'field2' => 'array2.2', 'field3' => 'array2.3' - ], \ArrayObject::ARRAY_AS_PROPS) - ], \ArrayObject::ARRAY_AS_PROPS); + ], ArrayObject::ARRAY_AS_PROPS) + ], ArrayObject::ARRAY_AS_PROPS); } /** diff --git a/Tests/Solr/Type/FieldMapperTypeTest.php b/Tests/Solr/Type/FieldMapperTypeTest.php index e4f47fd..2828f53 100644 --- a/Tests/Solr/Type/FieldMapperTypeTest.php +++ b/Tests/Solr/Type/FieldMapperTypeTest.php @@ -11,25 +11,19 @@ namespace Integrated\Bundle\SolrBundle\Tests\Solr\Type; -use ArrayObject; -use DateTime; - use Integrated\Bundle\SolrBundle\Solr\Type\FieldMapperType; use Integrated\Common\Converter\Container; use Integrated\Common\Converter\ContainerInterface; -use Integrated\Bundle\SolrBundle\Tests\Solr\Type\FieldMapperTypeTestTestObject as TestObject; +use Integrated\Bundle\SolrBundle\Tests\Fixtures\TestObject; /** - * @covers Integrated\Bundle\SolrBundle\Solr\Type\FieldMapperType + * @covers \Integrated\Bundle\SolrBundle\Solr\Type\FieldMapperType * * @author Jan Sanne Mulder */ class FieldMapperTypeTest extends \PHPUnit_Framework_TestCase { - /** - * - */ public function testInterface() { self::assertInstanceOf('Integrated\\Common\\Converter\\Type\\TypeInterface', $this->getInstance()); @@ -242,9 +236,6 @@ public function buildStringConversionProvider() ]; } - /** - * - */ public function testGetName() { self::assertEquals('integrated.fields', $this->getInstance()->getName()); diff --git a/composer.json b/composer.json index e4b6c69..1116144 100644 --- a/composer.json +++ b/composer.json @@ -14,19 +14,18 @@ "minimum-stability": "dev", "prefer-stable": true, "require": { - "php": ">=5.4", - "symfony/symfony": "~2.4", - "symfony/filesystem": "~2.6", - "integrated/library": "~0.3" + "php": ">=5.5", + "symfony/symfony": "~2.8 || ~3.0", + "integrated/library": "~0.7", + "integrated/content-bundle": "~0.7" }, "require-dev": { - "squizlabs/php_codesniffer": "2.*", - "phpunit/phpunit": "4.2.*" + "squizlabs/php_codesniffer": "^2.8", + "phpunit/phpunit": "^5.7" }, "autoload":{ - "psr-0":{ - "Integrated\\Bundle\\SolrBundle": "" + "psr-4":{ + "Integrated\\Bundle\\SolrBundle\\": "" } - }, - "target-dir": "Integrated/Bundle/SolrBundle" + } } \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml similarity index 100% rename from phpunit.xml.dist rename to phpunit.xml