From 4b0f1f7eb2cf7e02c55b9aab4f393b617e41c0b1 Mon Sep 17 00:00:00 2001 From: allejo Date: Thu, 8 Feb 2018 01:14:38 -0800 Subject: [PATCH] WIP for Page models --- controllers/PageController.php | 4 +- .../20180208072640_page_status_conversion.php | 79 ++++++++++++++ models/Page.php | 102 ++++++++---------- src/Form/Creator/PageFormCreator.php | 3 +- src/Model/BaseModel.php | 90 ++++++++++++++-- src/Model/Model.php | 4 +- src/QueryBuilder/QueryBuilderFlex.php | 17 ++- src/Twig/AppGlobal.php | 5 +- 8 files changed, 231 insertions(+), 73 deletions(-) create mode 100644 migrations/20180208072640_page_status_conversion.php diff --git a/controllers/PageController.php b/controllers/PageController.php index 930f206c..87db03e6 100644 --- a/controllers/PageController.php +++ b/controllers/PageController.php @@ -18,7 +18,9 @@ public function showDefaultAction() public function showAction(Page $page) { - return array("page" => $page); + return [ + 'page' => $page + ]; } public function createAction(Player $me, Request $request) diff --git a/migrations/20180208072640_page_status_conversion.php b/migrations/20180208072640_page_status_conversion.php new file mode 100644 index 00000000..27db5eb5 --- /dev/null +++ b/migrations/20180208072640_page_status_conversion.php @@ -0,0 +1,79 @@ +table('pages'); + $pagesTable + ->addColumn('is_unlisted', 'boolean', [ + 'after' => 'home', + 'null' => false, + 'default' => false, + 'comment' => 'Whether or not this page should be listed in the secondary navigation', + ]) + ->addColumn('is_draft', 'boolean', [ + 'after' => 'is_unlisted', + 'null' => false, + 'default' => false, + 'comment' => 'Whether or not the news article is a draft', + ]) + ->addColumn('is_deleted', 'boolean', [ + 'after' => 'is_draft', + 'null' => false, + 'default' => false, + 'comment' => 'Whether or not this entry has been soft-deleted', + ]) + ->update() + ; + + $this->query("UPDATE pages SET is_deleted = 1 WHERE status != 'live';"); + + $pagesTable + ->removeColumn('parent_id') + ->removeColumn('home') + ->removeColumn('status') + ->update() + ; + } + + public function down() + { + $pagesTable = $this->table('pages'); + $pagesTable + ->addColumn('parent_id', 'integer', [ + 'after' => 'id', + 'null' => true, + 'default' => null, + 'length' => 10, + 'comment' => 'The ID of the original page. If this column is set, then it is a revision', + ]) + ->addColumn('home', 'integer', [ + 'after' => 'author', + 'length' => 4, + 'null' => true, + 'default' => null, + 'comment' => '(Deprecated) Whether or not the page is the home page', + ]) + ->addColumn('status', 'set', [ + 'values' => ['live', 'revision', 'disabled', 'deleted'], + 'after' => 'home', + 'null' => false, + 'default' => 'live', + 'comment' => 'The status of this page', + ]) + ->update() + ; + + $this->query("UPDATE pages SET status = 'deleted' WHERE is_deleted = 1"); + + $pagesTable + ->removeColumn('is_unlisted') + ->removeColumn('is_draft') + ->removeColumn('is_deleted') + ->update() + ; + } +} diff --git a/models/Page.php b/models/Page.php index a8832fed..1bd74980 100644 --- a/models/Page.php +++ b/models/Page.php @@ -36,18 +36,13 @@ class Page extends AliasModel */ protected $author; - /** - * Whether the page is the home page - * @var bool - */ - protected $home; + protected $is_draft; + protected $is_unlisted; const DEFAULT_STATUS = 'live'; - /** - * The name of the database table used for queries - */ - const TABLE = "pages"; + const DELETED_COLUMN = 'is_deleted'; + const TABLE = 'pages'; const CREATE_PERMISSION = Permission::CREATE_PAGE; const EDIT_PERMISSION = Permission::EDIT_PAGE; @@ -62,8 +57,9 @@ protected function assignResult($page) $this->name = $page['name']; $this->alias = $page['alias']; $this->author = $page['author']; - $this->home = $page['home']; - $this->status = $page['status']; + $this->is_unlisted = $page['is_unlisted']; + $this->is_draft = $page['is_draft']; + $this->is_deleted = $page['is_deleted']; } /** @@ -118,24 +114,6 @@ public function getAuthor() return Player::get($this->author); } - /** - * Get the status of the page - * @return string - */ - public function getStatus() - { - return $this->status; - } - - /** - * Find out whether this is the homepage - * @return bool - */ - public function isHomePage() - { - return $this->home; - } - /** * Set the content of the page * @@ -147,17 +125,6 @@ public function setContent($content) return $this->updateProperty($this->content, "content", $content); } - /** - * Set the status of the page - * - * @param string $status One of "live", "revision" or "disabled" - * @return self - */ - public function setStatus($status) - { - return $this->updateProperty($this->status, "status", $status); - } - /** * Update the last edit timestamp * @return self @@ -173,20 +140,21 @@ public function updateEditTimestamp() * @param string $title The title of the page * @param string $content The content of page * @param int $authorID The ID of the author - * @param string $status Page status: 'live','disabled',or 'deleted' + * @param bool $is_draft Whether or not + * + * @since 0.11.0 The former enum $status parameter has been changed to the boolean $is_draft * * @return Page An object representing the page that was just created */ - public static function addPage($title, $content, $authorID, $status = "live") + public static function addPage($title, $content, $authorID, $is_draft = false) { - return self::create(array( + return self::create([ 'name' => $title, 'alias' => self::generateAlias($title), 'content' => $content, 'author' => $authorID, - 'home' => 0, - 'status' => $status, - ), array('created', 'updated')); + 'is_draft' => (bool)$is_draft + ], ['created', 'updated']); } /** @@ -244,18 +212,42 @@ public static function getLazyColumns() } /** - * Get a query builder for pages - * @return QueryBuilder + * {@inheritdoc} */ public static function getQueryBuilder() { - return new QueryBuilder('Page', array( - 'columns' => array( - 'name' => 'name', - 'status' => 'status' - ), - 'name' => 'name' - )); + return QueryBuilderFlex::createForModel(Page::class) + ->setNameColumn('name') + ; + } + + /** + * {@inheritdoc} + */ + public static function getActiveModels(QueryBuilderFlex &$qb) + { + $qb + ->whereNot(self::DELETED_COLUMN, '=', self::DELETED_VALUE) + ->whereNot('is_draft', '=', true) + ; + + return true; + } + + /** + * {@inheritdoc} + */ + public static function getEagerColumnsList() + { + return [ + 'id', + 'name', + 'alias', + 'author', + 'is_draft', + 'is_deleted', + 'is_unlisted', + ]; } /** diff --git a/src/Form/Creator/PageFormCreator.php b/src/Form/Creator/PageFormCreator.php index dc42f993..ffcba11d 100644 --- a/src/Form/Creator/PageFormCreator.php +++ b/src/Form/Creator/PageFormCreator.php @@ -79,8 +79,7 @@ public function enter($form) return \Page::addPage( $form->get('name')->getData(), $form->get('content')->getData(), - $this->me->getId(), - $form->get('status')->getData() + $this->me->getId() ); } } diff --git a/src/Model/BaseModel.php b/src/Model/BaseModel.php index cbdbde01..44651045 100644 --- a/src/Model/BaseModel.php +++ b/src/Model/BaseModel.php @@ -241,17 +241,17 @@ protected static function chooseModelFromDatabase($id) */ protected static function fetchColumnValues($id) { - $table = static::TABLE; - $columns = static::getEagerColumns(); - - $results = Database::getInstance() - ->query("SELECT $columns FROM $table WHERE id = ? LIMIT 1", array($id)); + $qb = QueryBuilderFlex::createForTable(static::TABLE); + $results = $qb + ->select(static::getEagerColumnsList()) + ->find($id) + ; if (count($results) < 1) { return null; } - return $results[0]; + return $results; } /** @@ -361,7 +361,7 @@ protected static function fetchIdsFrom($column, $possible_values, $negate = fals /** * Get the MySQL columns that will be loaded as soon as the model is created * - * @todo Make this protected + * @deprecated 0.10.2 Replaced by static::getEagerColumnsList() in 0.11.0 * * @param string $prefix The prefix that'll be prefixed to column names * @@ -379,6 +379,8 @@ public static function getEagerColumns($prefix = null) * This is done in order to reduce the time needed to load parameters that * will not be requested (e.g player activation codes or permissions) * + * @deprecated 0.10.2 Replaced by static::getLazyColumnsList() in 0.11.0 + * * @return string|null The columns in a format readable by MySQL or null to * fetch no columns at all */ @@ -390,6 +392,8 @@ protected static function getLazyColumns() /** * Get a formatted string with a comma separated column list with table/alias prefixes if necessary. * + * @deprecated 0.10.2 This function has been removed and is no longer required with the new query builder + * * @param string|null $prefix The table name or SQL alias to be prepend to these columns * @param array $columns The columns to format * @@ -404,6 +408,78 @@ protected static function formatColumns($prefix = null, $columns = ['*']) return (($prefix . '.') . implode(sprintf(',%s.', $prefix), $columns)); } + // + // Query building for models + // + + /** + * Get a query builder instance for this model. + * + * @throws BadMethodCallException When this function has not been configured for a particular model + * @throws Exception When no database has been configured for BZiON + * + * @since 0.11.0 The expected return type has been changed from QueryBuilder to QueryBuilderFlex + * @since 0.9.0 + * + * @return QueryBuilderFlex + */ + public static function getQueryBuilder() + { + throw new BadMethodCallException(sprintf('No Query Builder has been configured for the %s model', get_called_class())); + } + + /** + * Modify a QueryBuilderFlex instance to configure what conditions should be applied when fetching "active" entries. + * + * This function is called whenever a QueryBuilder calls `static::active()`. A reference to the QueryBuilderFlex + * instance is passed to allow you to add any necessary conditions to the query. This function should return true if + * the QueryBuilderInstance has been modified to stop propagation to other modifications; return false if nothing + * has been modified. + * + * @internal For use of QueryBuilderFlex when fetching active entries. + * + * @param \QueryBuilderFlex $qb A reference to the QBF to allow modifications. + * + * @since 0.11.0 + * + * @return bool Returns true if propagation should stop and the QueryBuilderFlex should not make further modifications + * in this regard. + */ + public static function getActiveModels(QueryBuilderFlex &$qb) + { + return false; + } + + /** + * Get the list of columns that should be eagerly loaded when a model is created from database results. + * + * @since 0.11.0 + * + * @return array + */ + public static function getEagerColumnsList() + { + return ['*']; + } + + /** + * Get the list of columns that need to be lazily loaded in a model. + * + * This function should return an empty array if no columns need to be lazily loaded. + * + * @since 0.11.0 + * + * @return array + */ + public static function getLazyColumnsList() + { + return []; + } + + // + // Model creation from the database + // + /** * Load all the parameters of the model that were not loaded during the first * fetch from the database diff --git a/src/Model/Model.php b/src/Model/Model.php index 8f1583aa..dba7b522 100644 --- a/src/Model/Model.php +++ b/src/Model/Model.php @@ -41,7 +41,7 @@ public function isDeleted() */ public function isActive() { - if (self::DELETED_COLUMN !== null) { + if (static::DELETED_COLUMN !== null) { return (!$this->is_deleted); } @@ -53,7 +53,7 @@ public function isActive() /** * Get the models's status * - * @deprecated 0.10.3 Use isDeleted() for checking for deleted models instead. + * @deprecated 0.11.0 Use isDeleted() for checking for deleted models instead. * * @return string */ diff --git a/src/QueryBuilder/QueryBuilderFlex.php b/src/QueryBuilder/QueryBuilderFlex.php index f5454d0a..ae4b8a70 100644 --- a/src/QueryBuilder/QueryBuilderFlex.php +++ b/src/QueryBuilder/QueryBuilderFlex.php @@ -4,7 +4,9 @@ use Pixie\QueryBuilder\QueryBuilderHandler; /** - * @since 0.10.3 + * The core query builder used across BZiON for creating and modifying queries for all of our entities. + * + * @since 0.11.0 */ class QueryBuilderFlex extends QueryBuilderHandler { @@ -105,13 +107,19 @@ public function active() if ($column === null) { @trigger_error( - 'The use of the status column is deprecated. Update this model to use the DELETED_* constants.', + sprintf('The use of the status column is deprecated. Update the %s model to use the DELETED_* constants.', get_called_class()), E_USER_DEPRECATED ); return $this->whereIn('status', $type::getActiveStatuses()); } + $stopPropagation = $type::getActiveModels($this); + + if ($stopPropagation) { + return $this; + } + return $this->whereNot($column, '=', $type::DELETED_VALUE); } @@ -121,6 +129,8 @@ public function active() * @param bool $fastFetch Whether to perform one query to load all the model data instead of fetching them one by * one * + * @throws \Pixie\Exception + * * @return void */ public function addToCache($fastFetch = true) @@ -195,9 +205,8 @@ public function getModels($fastFetch = false) { /** @var Model $type */ $type = $this->modelType; - $columns = ($fastFetch) ? $type::getEagerColumns() : ['*']; - $this->select($columns); + $this->select($type::getEagerColumnsList()); $queryObject = $this->getQuery(); $debug = new DatabaseQuery($queryObject->getSql(), $queryObject->getBindings()); diff --git a/src/Twig/AppGlobal.php b/src/Twig/AppGlobal.php index fd03bd65..421064b4 100644 --- a/src/Twig/AppGlobal.php +++ b/src/Twig/AppGlobal.php @@ -196,8 +196,9 @@ public function getSocket() public function getPages() { return \Page::getQueryBuilder() - ->where('status')->equals('live') - ->getModels($fast = true); + ->active() + ->getModels($fast = true) + ; } /**