setDescription('Build permalinks'); $this->addOption('databox', null, InputOption::VALUE_REQUIRED, 'Mandatory : The id (or dbname or viewname) of the databox'); $this->addOption('clear_cache', null, InputOption::VALUE_NONE, 'Clear the cache before running'); $this->addOption('create', null, InputOption::VALUE_NONE, 'Create missing permalinks'); $this->addOption('force_create', null, InputOption::VALUE_NONE, '(re)create all permalinks (enforce clear_cache)'); $this->addOption('name', null, InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Create only for those subdefs, ex. "thumbnail,preview", default=ALL'); $this->addOption('prune', null, InputOption::VALUE_NONE, 'Delete permalinks on non-existing subdefs'); $this->addOption('dry', null, InputOption::VALUE_NONE, 'dry run, list but don\'t act'); $this->addOption('show_sql', null, InputOption::VALUE_NONE, 'show sql'); } /** * merge options so one can mix csv-option and/or multiple options * ex. with keepUnique = false : --opt=a,b --opt=c --opt=b ==> [a,b,c,b] * ex. with keepUnique = true : --opt=a,b --opt=c --opt=b ==> [a,b,c] * * @param InputInterface $input * @param string $optionName * @param int $option * @return array */ private function getOptionAsArray(InputInterface $input, $optionName, $option) { $ret = []; foreach($input->getOption($optionName) as $v0) { foreach(explode(',', $v0) as $v) { $v = trim($v); if($option & self::OPTION_ALL_VALUES || !in_array($v, $ret)) { $ret[] = $v; } } } return $ret; } /** * print a string if verbosity >= verbose (-v) * @param string $s */ private function verbose($s) { if($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { $this->output->write($s); } } /** * sanity check the cmd line options * * @param InputInterface $input * @param OutputInterface $output * @return bool */ protected function sanitizeArgs(InputInterface $input, OutputInterface $output) { $argsOK = true; // find the databox / collection by id or by name $this->databox = null; if(($d = $input->getOption('databox')) !== null) { $d = trim($d); foreach ($this->container->getDataboxes() as $db) { if ($db->get_sbas_id() == (int)$d || $db->get_viewname() == $d || $db->get_dbname() == $d) { $this->databox = $db; $this->connection = $db->get_connection(); break; } } if ($this->databox == null) { $output->writeln(sprintf("Unknown databox \"%s\"", $input->getOption('databox'))); $argsOK = false; } } else { $output->writeln(sprintf("Missing mandatory options --databox")); $argsOK = false; } $this->clear_cache = $input->getOption('clear_cache') ? true : false; $this->show_sql = $input->getOption('show_sql') ? true : false; $this->dry = $input->getOption('dry') ? true : false; $this->create = $input->getOption('create') ? true : false; $this->force_create = $input->getOption('force_create') ? true : false; $this->prune = $input->getOption('prune') ? true : false; $this->names = $this->getOptionAsArray($input, 'name', self::OPTION_DISTINT_VALUES); if(!$this->create && !$this->force_create && !$this->prune) { $output->writeln(sprintf("Spercifiy at least one action : --prune, --create, --force_create")); $argsOK = false; } if($this->dry && $this->force_create) { $output->writeln(sprintf("--dry can't be used with --force_create")); $argsOK = false; } if($this->force_create) { $this->clear_cache = true; } return $argsOK; } /** * {@inheritdoc} */ protected function doExecute(InputInterface $input, OutputInterface $output) { if(!$this->sanitizeArgs($input, $output)) { return -1; } $this->input = $input; $this->output = $output; $progress = null; if($this->clear_cache) { $output->writeln(sprintf("=== clear cache ===")); $command = $this->getApplication()->find('system:clear-cache'); $return_code = $command->run(new ArrayInput([]), $output); if($return_code !== 0) { $output->writeln("system:clear-cache failed, quit."); } } /** * prune */ if($this->prune) { $output->writeln(sprintf("=== prune ===")); $s = "`permalinks` p LEFT JOIN `subdef` s USING(subdef_id) WHERE ISNULL(s.subdef_id)"; $sqlDel = "DELETE p FROM " . $s; if($this->show_sql) { $this->output->writeln($sqlDel); } $sqlCount = "SELECT COUNT(*) AS n FROM " . $s; $stmt = $this->connection->executeQuery($sqlCount); $row = $stmt->fetch(PDO::FETCH_ASSOC); $stmt->closeCursor(); $output->writeln(sprintf("%d permalinks to be deleted", $row['n'])); if($this->dry) { $output->writeln(sprintf("dry : not executed")); } else { $stmt = $this->connection->executeQuery($sqlDel); $output->writeln(sprintf("%d permalinks deleted", $stmt->rowCount())); } } /* * force_create : delete all plinks to force recreate */ if($this->force_create) { $output->writeln(sprintf("=== force create ===")); $s = "`permalinks` p INNER JOIN `subdef` s USING(subdef_id)"; if (!empty($this->names)) { $n = join(',', array_map(function ($v) { return $this->connection->quote($v); }, $this->names)); $s .= " WHERE s.name IN(" . $n . ")"; } $sqlDel = "DELETE p FROM " . $s; if($this->show_sql) { $this->output->writeln($sqlDel); } $sqlCount = "SELECT COUNT(*) AS n FROM " . $s; $stmt = $this->connection->executeQuery($sqlCount); $row = $stmt->fetch(PDO::FETCH_ASSOC); $stmt->closeCursor(); $output->writeln(sprintf("%d permalinks to be deleted", $row['n'])); if($this->dry) { $output->writeln(sprintf("dry : not executed")); } else { $stmt = $this->connection->executeQuery($sqlDel); $output->writeln(sprintf("%d permalinks deleted", $stmt->rowCount())); } } /** * create */ if($this->create || $this->force_create) { $output->writeln(sprintf("=== create ===")); $s = "subdef s LEFT JOIN permalinks p USING(subdef_id)"; $w = []; if(!$this->force_create) { $w[] = "ISNULL(p.subdef_id)"; } if (!empty($this->names)) { $n = join(',', array_map(function ($v) { return $this->connection->quote($v); }, $this->names)); $w[] = "s.name IN(" . $n . ")"; } if(($w = join(' AND ', $w)) !== '') { $s .= " WHERE " . $w; } $sqlSelect = "SELECT s.record_id, GROUP_CONCAT(s.subdef_id) AS subdef_ids FROM " . $s . " GROUP BY record_id"; if ($this->show_sql) { $this->output->writeln($sqlSelect); } $sqlCount = "SELECT COUNT(*) AS n FROM " . $s; $stmt = $this->connection->executeQuery($sqlCount); $row = $stmt->fetch(PDO::FETCH_ASSOC); $stmt->closeCursor(); $output->writeln(sprintf("%d permalinks to be created", $row['n'])); if ($this->dry) { $output->writeln(sprintf("dry : not executed")); } else { $stmt = $this->connection->executeQuery($sqlSelect); $time_start = microtime(true); $duration = 0.0; $n_created = 0; $lmax = 0; // max length of msg (displayed on same line), useful to clear the line after run $app = $this->getContainer(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { $record_id = $row['record_id']; $subdef_ids = $row['subdef_ids']; $record = null; try { $record = $this->databox->get_record($record_id); } catch(\Exception $e) { $output->writeln(sprintf("Failed to get record %s, skip.", $record_id )); } if($record) { try { /* * really this will create the plink if it does not exists... * ... so we can't test existance that way. */ //$record->get_subdef($name)->get_permalink(); /* * todo : use permalink adapter */ $subdef_ids = explode(',', $subdef_ids); $n_created += media_Permalink_Adapter::createFromRecord($app, $record, $subdef_ids); } catch (\Exception $e) { // cant get record ? ignore } } $duration = microtime(true) - $time_start ; if($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { $lmax = max($lmax, strlen($msg = sprintf("rid: %-6s %4.1f\r", $record_id, $n_created / $duration))); $output->write($msg, $this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE); } } $stmt->closeCursor(); $output->write(str_repeat(' ', $lmax) . "\r"); // clear the line $duration = max(.000001, $duration); // avoid division by 0 (anyway n_created is 0 in this case) $output->writeln(sprintf("%s permalinks created in %.1f seconds : %.1f p/s", $n_created, $duration, $n_created / $duration )); } } return 0; } }