Perforce Chronicle 2012.2/486814
API Documentation

P4Cms_Categorization_CategoryAbstract Class Reference

This class facilitates the creation of categories, including storage of arbitrary metadata about the category, and associating arbitrary data with each category. More...

Inheritance diagram for P4Cms_Categorization_CategoryAbstract:
P4Cms_Record P4Cms_Record_Connected P4Cms_Model P4Cms_ModelInterface Category_Model_Category

List of all members.

Public Member Functions

 addEntries ($entries)
 Add multiple entries to this category.
 addEntry ($entry)
 Add an entry to this category.
 delete ($description=null)
 Delete a category.
 deleteEntries ($entries)
 Delete multiple entries from this category.
 deleteEntry ($entry)
 Delete an entry from this category.
 getAncestorIds ()
 Get the ids of this categories ancestors ordered from the top down (greatest ancestor to direct parent).
 getAncestors ()
 Retrieves the ancestors for this category ordered from the top down (greatest ancestor to direct parent).
 getBaseId ()
 Get the base id - the trailing portion (basename) of the id.
 getChildren ($recursive=false)
 Retrieves child categories within the specified path, if any.
 getDepth ()
 Get the depth of this category in the hierarchy.
 getDescription ()
 Get the current description or null if none set.
 getEntries (array $options=array())
 Retrieve the entries within this category.
 getParent ()
 Retrieves the parent category for this category.
 getParentId ()
 Get the id of this category's parent category.
 getTitle ()
 Get the category title or its base id if no title available.
 hasChildren ($recursive=false)
 Determines whether the specified category contains child categories.
 hasEntries ($recursive=false)
 Determines whether the current category has any entries.
 hasEntry ($id)
 Determines if the specified entry exists in this category.
 hasParent ()
 Determines whether the current category has a parent category in storage.
 save ($description=null)
 Extend save to verify that category ancestry exists.
 setDescription ($description)
 Update the description with a new value.
 setId ($id)
 Set the id for this category category.
 setTitle ($title)
 Update the title with a new value.

Static Public Member Functions

static decodeEntryId ($encoded)
 Decode an encoded entry id.
static depotFileToId ($depotFile, P4Cms_Record_Adapter $adapter=null)
 Given a category filespec in depotFile syntax, determine the id.
static encodeEntryId ($id)
 Encode an entry id so that it's safe to place in a filesystem.
static fetchAll ($query=null, P4Cms_Record_Adapter $adapter=null)
 Extend parent to limit query to categories (excluding entries).
static fetchAllByEntry ($item, P4Cms_Record_Adapter $adapter=null)
 Get all categories that contain the given entry.
static fetchIdsByEntry ($item, P4Cms_Record_Adapter $adapter=null)
 Get the ids of all categories that contain the given entry.
static idToFilespec ($id, P4Cms_Record_Adapter $adapter=null)
 Given a category id, determine the corresponding filespec.
static isNestingAllowed ()
 Reports whether the current category allows nesting of categories.
static move ($sourceId, $targetId, P4Cms_Record_Adapter $adapter=null)
 Move/rename a category.
static setEntryCategories ($item, $newCategoryIds, P4Cms_Record_Adapter $adapter=null)
 Set the categories that an entry resides in.

Public Attributes

const CATEGORY_FILENAME = '.index'
 Defines the filename to contain category metadata.
const ENTRY_PREFIX = '_'
 Defines the prefix for encoded category entry ids.
const OPTION_RECURSIVE = 'recursive'

Protected Member Functions

 _acceptId ($item, $errorPrefix= '')
 Accept ids passed as strings or objects with getId() method.
 _adjustEntries ($action, $description, $entries)
 Adjust the entries by performing the specified action (add or delete).
 _canAcceptId ($item)
 Determines whether an id can be extracted from the argument.
 _checkIdSet ($action= '')
 Checks whether the current instance has an id set.
 _getEntryRecordId ($id)
 Get the record id for the given entry id.

Static Protected Member Functions

static _checkNestability ()
 Check whether this category class permits nesting.

Static Protected Attributes

static $_fields
 Specify the fields for category metadata.
static $_nestingAllowed = false
 Specifies whether the concrete category allows nested categories.
static $_storageSubPath = null
 Specifies the sub-path to use for storage of category data.

Detailed Description

This class facilitates the creation of categories, including storage of arbitrary metadata about the category, and associating arbitrary data with each category.

Categories can provide nesting, similar to folders in a filesystem, or can be flat, such as a list of tags.

Provides category associations/storage.

Typical usage:

$category = new ConcreteCategory; $category->setId('/') ->setLabel('My Directory') ->save(); $entries = $category->getEntries();

Copyright:
2011-2012 Perforce Software. All rights reserved
License:
Please see LICENSE.txt in top-level folder of this distribution.
Version:
2012.2/486814

Member Function Documentation

P4Cms_Categorization_CategoryAbstract::_acceptId ( item,
errorPrefix = '' 
) [protected]

Accept ids passed as strings or objects with getId() method.

Override in a concrete class to implement the required id extraction. Note: ids passed as integers will be converted to strings.

Parameters:
string | object$iteman id, or an object that has an id,
string$errorPrefixa prefix to include in exceptions, if necessary.
Exceptions:
InvalidArgumentExceptionif $item is not a string or object with getId().
    {
        $id = '';
        if (is_string($item) || is_int($item)) {
            $id = (string) $item;
        } else if (is_object($item)) {
            if (method_exists($item, 'getId')) {
                $id = $item->getId();
            } else {
                throw new InvalidArgumentException(
                    $errorPrefix .'the provided object does not have a getId() method.'
                );
            }
        } else {
            throw new InvalidArgumentException(
                $errorPrefix .'the provided entry is not a string or object with a getId() method.'
            );
        }

        return $id;
    }
P4Cms_Categorization_CategoryAbstract::_adjustEntries ( action,
description,
entries 
) [protected]

Adjust the entries by performing the specified action (add or delete).

Parameters:
string$actionthe action to perform (add or delete).
string$descriptionthe description to supply to perforce.
array$entriesan array of strings or entry objects to associate with the current category.
Returns:
P4Cms_Categorization_CategoryAbstract provides fluent interface.
Exceptions:
P4Cms_Categorization_Exceptionif category id not set.
InvalidArgumentExceptionif $entries is not an array.
    {
        // ensure action is valid.
        if (!in_array($action, array('add', 'delete'))) {
            throw new InvalidArgumentException(
                "Cannot '$action' entries. Action must be add or delete."
            );
        }

        // ensure we have an id.
        $catId = $this->_checkIdSet("$action entries");

        // validate entries
        if (!is_array($entries)) {
            throw new InvalidArgumentException(
                "Cannot $action entries; you must provide an array of entries."
            );
        }
        foreach ($entries as $entry) {
            if (is_string($entry) || $this->_canAcceptId($entry)) {
                continue;
            }
            throw new InvalidArgumentException(
                "Cannot $action entries; all entries must either be strings or known entry types."
            );
        }

        // begin a batch if we're not already in one.
        $adapter = $this->getAdapter();
        $batch   = !$adapter->inBatch()
            ? $adapter->beginBatch($description)
            : false;

        // add the entries
        foreach ($entries as $entry) {
            $id = $this->_acceptId($entry, "Cannot $action entry; ");

            // skip existing entries for add and
            // non-existant entries for delete.
            $shouldExist = ($action == 'add');
            if ($shouldExist == $this->hasEntry($id)) {
                continue;
            }

            // adjust method call to add or delete as appropriate.
            $method = $action == 'add' ? 'save' : 'delete';

            // setup entry record.
            $record = new P4Cms_Record;
            $record->setAdapter($this->getAdapter())
                   ->setId($this->_getEntryRecordId($id))
                   ->$method();
        }

        // commit batch if we made it.
        if ($batch) {
            $adapter->commitBatch();
        }

        return $this;
    }
P4Cms_Categorization_CategoryAbstract::_canAcceptId ( item) [protected]

Determines whether an id can be extracted from the argument.

Parameters:
mixed$iteman id, or other structure that has an id.
Returns:
bool true if an id can be extracted, false otherwise.
    {
        try {
            $id = $this->_acceptId($item);
            return (isset($id) && strlen($id)) ? true : false;
        } catch (Exception $e) {
            return false;
        }
    }
P4Cms_Categorization_CategoryAbstract::_checkIdSet ( action = '') [protected]

Checks whether the current instance has an id set.

Parameters:
string$actionIdentifies the action in the exception, if required.
Exceptions:
P4Cms_Categorization_Exceptionif the id is not set.
    {
        if ($this->getId() === null) {
            if (strlen($action)) {
                $action = " $action";
            }
            throw new P4Cms_Categorization_Exception(
                "Cannot$action; category id is not set."
            );
        }

        return $this->getId();
    }
static P4Cms_Categorization_CategoryAbstract::_checkNestability ( ) [static, protected]

Check whether this category class permits nesting.

Exceptions:
P4Cms_Categorization_ExceptionIf nesting is not allowed.
    {
        if (!static::isNestingAllowed()) {
            throw new P4Cms_Categorization_Exception(
                "This category does not permit nesting."
            );
        }
    }
P4Cms_Categorization_CategoryAbstract::_getEntryRecordId ( id) [protected]

Get the record id for the given entry id.

Parameters:
string$idthe entry id to get the filespec for.
Returns:
string the record id for the given entry id.
    {
        // ensure we have a category id.
        $this->_checkIdSet('get entry record id');

        return static::$_storageSubPath
            . '/' . $this->getId()
            . '/' . static::encodeEntryId($id);
    }
P4Cms_Categorization_CategoryAbstract::addEntries ( entries)

Add multiple entries to this category.

Parameters:
array$entriesan array of strings or entry objects to associate with this category.
Returns:
P4Cms_Categorization_CategoryAbstract provides fluent interface.
Exceptions:
P4Cms_Categorization_Exceptionif category id not set.
InvalidArgumentExceptionif $entries is not an array.
    {
        return $this->_adjustEntries(
            'add',
            'Added entries to ' . $this->getId() . '.',
            $entries
        );
    }
P4Cms_Categorization_CategoryAbstract::addEntry ( entry)

Add an entry to this category.

Parameters:
mixed$entryan entry id, or a known entry type, for association with the current category.
Returns:
P4Cms_Categorization_CategoryAbstract provide fluent interface.
Exceptions:
InvalidArgumentExceptionif $entry is not a string or known entry type.
    {
        return $this->addEntries(array($entry));
    }
static P4Cms_Categorization_CategoryAbstract::decodeEntryId ( encoded) [static]

Decode an encoded entry id.

Parameters:
string$encodedThe encoded id to decode.
Returns:
string Decoded entry id.
Exceptions:
InvalidArgumentExceptionif $encoded is not set, has 0 length, or cannot be successfully decoded.
    {
        if (!isset($encoded) || strlen($encoded) == 0) {
            throw new InvalidArgumentException(
                'Cannot decode entry id; encoded id not set or has no length.'
            );
        }

        $id = null;
        try {
            $id = pack('H*', ltrim($encoded, static::ENTRY_PREFIX));
        } catch (Exception $e) {
            throw new InvalidArgumentException(
                "Cannot decode entry id; encoded id contains invalid characters."
            );
        }

        return $id;
    }
P4Cms_Categorization_CategoryAbstract::delete ( description = null)

Delete a category.

Extends parent to delete entries and sub-categories.

Parameters:
string$descriptionoptional - a description of the change.
Returns:
P4Cms_Categorization_CategoryAbstract provides fluent interface.

Reimplemented from P4Cms_Record.

    {
        $id = $this->_checkIdSet('delete category');

        // ensure id exists.
        if (!static::exists($id)) {
            throw new P4Cms_Categorization_Exception(
                "Cannot delete category. Category does not exist."
            );
        }

        $adapter    = $this->getAdapter();
        $connection = $adapter->getConnection();
        $filespec   = static::getStoragePath($adapter) . "/" . $id . "/...";

        // revert and delete this entire category (-v deletes without syncing).
        $connection->run('revert', $filespec);
        $connection->run('delete', array('-v', $filespec));

        // if we're in a batch, reopen in batch change, else submit.
        if ($adapter->inBatch()) {
            $connection->run('reopen', array('-c', $adapter->getBatchId(), $filespec));
        } else {
            if (!$description) {
                $description = "Deleted '" . static::$_storageSubPath . "' record.";
            }
            $connection->run('submit', array('-d', $description, $filespec));
        }

        return $this;
    }
P4Cms_Categorization_CategoryAbstract::deleteEntries ( entries)

Delete multiple entries from this category.

Parameters:
array$entriesan array of strings or record objects to remove association from the current category.
Returns:
P4Cms_Categorization_CategoryAbstract provide fluent interface.
Exceptions:
InvalidArgumentExceptionif $entries is not an array.
    {
        return $this->_adjustEntries(
            'delete',
            'Deleted entries from ' . $this->getId() . '.',
            $entries
        );
    }
P4Cms_Categorization_CategoryAbstract::deleteEntry ( entry)

Delete an entry from this category.

Parameters:
mixed$entryan entry id, or a known entry type, for removal from this category.
Returns:
P4Cms_Categorization_CategoryAbstract provide fluent interface.
Exceptions:
InvalidArgumentExceptionif $entry is not a string or known entry type.
    {
        return $this->deleteEntries(array($entry));
    }
static P4Cms_Categorization_CategoryAbstract::depotFileToId ( depotFile,
P4Cms_Record_Adapter adapter = null 
) [static]

Given a category filespec in depotFile syntax, determine the id.

Parameters:
string$depotFilea record depotFile.
P4Cms_Record_Adapter$adapteroptional - storage adapter to use.
Returns:
string|int the id portion of the depotFile file spec.

Reimplemented from P4Cms_Record.

    {
        // ensure depotFile is a category file.
        if (basename($depotFile) != static::CATEGORY_FILENAME) {
            throw new InvalidArgumentException(
                "Cannot get category id. Given depot file is not a valid category."
            );
        }

        return dirname(parent::depotFileToId($depotFile, $adapter));
    }
static P4Cms_Categorization_CategoryAbstract::encodeEntryId ( id) [static]

Encode an entry id so that it's safe to place in a filesystem.

Encoded ids are modified base64 (replacing slashes '/' with dashes '-') with an ENTRY_PREFIX character.

Parameters:
string$idThe id to encode.
Returns:
string Encoded id suitable for filesystem storage.
Exceptions:
InvalidArgumentExceptionif $id is not set, or has 0 length.
    {
        if (!isset($id) || strlen($id) == 0) {
            throw new InvalidArgumentException(
                'Cannot encode entry id; id not set or has no length.'
            );
        }

        return static::ENTRY_PREFIX . bin2hex($id);
    }
static P4Cms_Categorization_CategoryAbstract::fetchAll ( query = null,
P4Cms_Record_Adapter adapter = null 
) [static]

Extend parent to limit query to categories (excluding entries).

See parent implementation for full description of options.

Parameters:
P4Cms_Record_Query | array | null$queryoptional - query options to augment result.
P4Cms_Record_Adapter$adapteroptional - storage adapter to use.
Returns:
P4Cms_Model_Iterator all records of this type.

Reimplemented from P4Cms_Record.

    {
        if (!$query instanceof P4Cms_Record_Query && !is_array($query) && !is_null($query)) {
            throw new InvalidArgumentException(
                'Query must be a P4Cms_Record_Query, array or null'
            );
        }

        // normalize array input to a query
        if (is_array($query)) {
            $query = new P4Cms_Record_Query($query);
        }

        // if null query given, make a new one.
        $query = $query ?: new P4Cms_Record_Query;

        // return all categories if we haven't been given any path/id limits.
        if ($query->getPaths() === null && !$query->getIds()) {
            $query->addPath('...');
        }

        // manipulate the fetch-by-path so we search for the category file(s).
        $newPaths = array();
        foreach ($query->getPaths() as $path) {
            $newPaths[] = $path .'/'. static::CATEGORY_FILENAME;
        }
        $query->setPaths($newPaths);

        // category files push depth down a level, adjust max depth if set.
        if ($query->getMaxDepth() !== null && $query->getMaxDepth() >= 0) {
            $query->setMaxDepth($query->getMaxDepth() + 1);
        }

        return parent::fetchAll($query, $adapter);
    }
static P4Cms_Categorization_CategoryAbstract::fetchAllByEntry ( item,
P4Cms_Record_Adapter adapter = null 
) [static]

Get all categories that contain the given entry.

If you just want category ids (not objects), use fetchIdsByEntry.

Parameters:
mixed$iteman id, or a known entry type to search for.
P4Cms_Record_Adapter$adapteroptional - storage adapter to use.
Returns:
P4Cms_Model_Iterator categories containing the $item entry.
    {
        $query = new P4Cms_Record_Query;
        $query->addPaths(static::fetchIdsByEntry($item, $adapter));
        return static::fetchAll($query, $adapter);
    }
static P4Cms_Categorization_CategoryAbstract::fetchIdsByEntry ( item,
P4Cms_Record_Adapter adapter = null 
) [static]

Get the ids of all categories that contain the given entry.

Parameters:
mixed$iteman id, or a known entry type to search for.
P4Cms_Record_Adapter$adapteroptional - storage adapter to use.
Returns:
array ids of categories containing the $item entry.
    {
        $tempCategory = new static;
        if (!isset($item) || !$tempCategory->_canAcceptId($item)) {
            throw new InvalidArgumentException(
                "Cannot get categories; the entry must either be a string or known entry type."
            );
        }

        // if no adapter given, use default.
        $adapter = $adapter ?: static::getDefaultAdapter();

        // determine set of categories containing entry using embedded wildcard.
        $filespec = static::getDepotStoragePath($adapter)
                  . '/.../' . static::encodeEntryId($item);
        $query = P4_File_Query::create()->addFilespec($filespec)->setFilter('^headAction=...delete');
        $files = P4_File::fetchAll($query, $adapter->getConnection());

        // derive ids from file names.
        $categories = array();
        foreach ($files as $file) {
            $categories[] = static::depotFileToId(
                dirname($file->getFilespec()) . '/' . static::CATEGORY_FILENAME,
                $adapter
            );
        }

        return $categories;
    }
P4Cms_Categorization_CategoryAbstract::getAncestorIds ( )

Get the ids of this categories ancestors ordered from the top down (greatest ancestor to direct parent).

Returns:
array the ids of this category's ancestors.
Exceptions:
P4Cms_Categorization_Exceptionif no id is set.
    {
        $this->_checkIdSet('get ancestor ids');

        // no ancestry if depth zero.
        if ($this->getDepth() === 0) {
            return array();
        }

        // extract ancestors from parent id.
        $ancestors = array();
        $segments  = explode('/', $this->getParentId());
        foreach ($segments as $segment) {
            $ancestorId  = (isset($ancestorId) ? $ancestorId . '/' : '') . $segment;
            $ancestors[] = $ancestorId;
        }

        return $ancestors;
    }
P4Cms_Categorization_CategoryAbstract::getAncestors ( )

Retrieves the ancestors for this category ordered from the top down (greatest ancestor to direct parent).

Returns:
P4Cms_Model_Iterator all of the ancestors for this category.
    {
        $query = new P4Cms_Record_Query;
        $query->addPaths($this->getAncestorIds());
        return static::fetchAll($query, $this->getAdapter());
    }
P4Cms_Categorization_CategoryAbstract::getBaseId ( )

Get the base id - the trailing portion (basename) of the id.

Returns:
string the basename of the id.
    {
        return basename($this->getId());
    }
P4Cms_Categorization_CategoryAbstract::getChildren ( recursive = false)

Retrieves child categories within the specified path, if any.

Parameters:
bool$recursiveoptional - collect children recursively if true; defaults to false.
Returns:
P4Cms_Model_Iterator An iterator of the child categories.
    {
        // perform validations
        static::_checkNestability();
        $id = $this->_checkIdSet('get children');

        // fetch all categories under this category.
        $selector = $recursive ? '...' : '*';
        return static::fetchAll(
            P4Cms_Record_Query::create()->addPath($id . '/' . $selector),
            $this->getAdapter()
        );
    }
P4Cms_Categorization_CategoryAbstract::getDepth ( )

Get the depth of this category in the hierarchy.

Categories at the root of the tree will have a depth of zero. The depth is equivalent to the number of ancestors a category has.

Returns:
int the depth of this category in the tree (zero for top-level categories).
Exceptions:
P4Cms_Category_Exceptionif this category class does not permit nesting.
    {
        // ensure nesting allowed.
        static::_checkNestability();

        return substr_count($this->getId(), '/');
    }
P4Cms_Categorization_CategoryAbstract::getDescription ( )

Get the current description or null if none set.

Returns:
string|null The description of this category, or null.
    {
        return $this->_getValue('description');
    }
P4Cms_Categorization_CategoryAbstract::getEntries ( array $  options = array())

Retrieve the entries within this category.

By default this will return a list of unique entry identifiers. If dereference is set to true, entry ids get resolved to their original form (e.g. objects).

Parameters:
array$optionsoptions to influence fetching entries, recognized keys are: OPTION_RECURSIVE - whether to include entries in sub-categories sort - if true then entries will be sorted
Returns:
array all unique entries within this category.

Reimplemented in Category_Model_Category.

    {
        $id        = $this->_checkIdSet('get entries');
        $adapter   = $this->getAdapter();
        $recursive = isset($options[static::OPTION_RECURSIVE]) && $options[static::OPTION_RECURSIVE];
        $sort      = isset($options['sort']) && $options['sort'];

        // fetch entries in this category - exclude deleted and
        // category metadata files and adjust wildcard for recursive.
        $filespec = dirname(static::idToFilespec($id, $adapter));
        $wildcard = $recursive ? '...' : '*';
        $filter   = '^headAction=...delete ^depotFile=...' . static::CATEGORY_FILENAME;
        $query    = P4_File_Query::create()
                    ->addFilespec($filespec .'/'. $wildcard)
                    ->setFilter($filter);
        $files    = P4_File::fetchAll($query, $adapter->getConnection());

        // filenames are encoded entry ids.
        $entries = array();
        foreach ($files as $file) {
            $entries[] = static::decodeEntryId($file->getBasename());
        }

        // ensure entries are unique.
        $entries = array_unique($entries);

        // sort entries.
        if ($sort) {
            sort($entries);
        }

        return $entries;
    }
P4Cms_Categorization_CategoryAbstract::getParent ( )

Retrieves the parent category for this category.

Returns:
P4Cms_Categorization_CategoryAbstract the parent category.
Exceptions:
P4Cms_Categorization_Exceptionif has no parent or category id does not exist.
    {
        if ($this->getDepth() === 0) {
            throw new P4Cms_Categorization_Exception(
                "Cannot get parent. This category has no parent."
            );
        }

        return static::fetch($this->getParentId(), null, $this->getAdapter());
    }
P4Cms_Categorization_CategoryAbstract::getParentId ( )

Get the id of this category's parent category.

Returns null for top-level categories.

Returns:
string the id of this category's parent.
Exceptions:
P4Cms_Categorization_Exceptionif no id is set.
    {
        $this->_checkIdSet('get parent category');

        if ($this->getDepth() > 0) {
            return dirname($this->getId());
        } else {
            return null;
        }
    }
P4Cms_Categorization_CategoryAbstract::getTitle ( )

Get the category title or its base id if no title available.

Returns:
string|null The title of this object, or ID if no title available.
    {
        $title = strlen($this->_getValue('title'))
            ? $this->_getValue('title')
            : $this->getBaseId();

        return $title === null ?: (string) $title;
    }
P4Cms_Categorization_CategoryAbstract::hasChildren ( recursive = false)

Determines whether the specified category contains child categories.

Parameters:
bool$recursiveoptional - collect children recursively if true; defaults to false.
Returns:
bool true if at least one child category exists, false otherwise.
    {
        return (bool) count($this->getChildren($recursive)) > 0;
    }
P4Cms_Categorization_CategoryAbstract::hasEntries ( recursive = false)

Determines whether the current category has any entries.

Parameters:
bool$recursiveoptional - evaluate entries recursively if true;
Returns:
bool true if entries exist, false otherwise.
    {
        return (bool) count($this->getEntries(array(static::OPTION_RECURSIVE => $recursive)));
    }
P4Cms_Categorization_CategoryAbstract::hasEntry ( id)

Determines if the specified entry exists in this category.

Parameters:
string$idthe id of the entry to check for.
Returns:
bool true if the entry is in this category; false otherwise.
    {
        $this->_checkIdSet('check for entry');

        return P4Cms_Record::exists(
            $this->_getEntryRecordId($id),
            null,
            $this->getAdapter()
        );
    }
P4Cms_Categorization_CategoryAbstract::hasParent ( )

Determines whether the current category has a parent category in storage.

If you just want to check if the category looks like it should have a parent, use getDepth().

Returns:
bool true if parent exists, false otherwise.
    {
        return ($this->getDepth())
            ? static::exists($this->getParentId())
            : false;
    }
static P4Cms_Categorization_CategoryAbstract::idToFilespec ( id,
P4Cms_Record_Adapter adapter = null 
) [static]

Given a category id, determine the corresponding filespec.

This overrides P4Cms_Record's mapping to force the use of a particular filename.

Parameters:
string$idthe category id to get the filespec for.
P4Cms_Record_Adapter$adapteroptional - storage adapter to use.
Returns:
string the path used to store this category in perforce.

Reimplemented from P4Cms_Record.

    {
        return parent::idToFilespec($id, $adapter) . '/' . static::CATEGORY_FILENAME;
    }
static P4Cms_Categorization_CategoryAbstract::isNestingAllowed ( ) [static]

Reports whether the current category allows nesting of categories.

Returns:
bool true if nesting allowed, false otherwise.
    {
        return (bool) static::$_nestingAllowed;
    }
static P4Cms_Categorization_CategoryAbstract::move ( sourceId,
targetId,
P4Cms_Record_Adapter adapter = null 
) [static]

Move/rename a category.

Parameters:
string$sourceIdThe category id to be moved/renamed
string$targetIdThe category destination id.
P4Cms_Record_Adapter$adapteroptional - storage adapter to use.
Exceptions:
InvalidArgumentExceptionif $sourceId is null or does not exist.
InvalidArgumentExceptionif $targetId is null or already exists.
InvalidArgumentExceptionif $sourceId or $targetId == '' (aka root).
InvalidArgumentExceptionif $targetId is a sub-category of $sourceId.
P4Cms_Categorization_Exceptionif the move is unsuccessful for some other reason.
    {
        // if no adapter given, use default.
        $adapter = $adapter ?: static::getDefaultAdapter();

        if (!isset($sourceId) || !isset($targetId)) {
            throw new InvalidArgumentException(
                'Cannot move category; both the source and target category must be specified.'
            );
        }
        if ($sourceId == '' || $targetId == '') {
            throw new InvalidArgumentException(
                'Cannot move category; neither the source or target category can be "".'
            );
        }
        if (!static::exists($sourceId, null, $adapter)) {
            throw new InvalidArgumentException(
                'Cannot move category; source category does not exist.'
            );
        }
        if (static::exists($targetId, null, $adapter)) {
            throw new InvalidArgumentException(
                'Cannot move category; target category already exists.'
            );
        }
        if (strpos($targetId, $sourceId . '/') === 0) {
            throw new InvalidArgumentException(
                'Cannot move category; target category is within source category.'
            );
        }

        // if we're not in a batch, setup a change to contain the moves
        if (!$adapter->inBatch()) {
            $change = new P4_Change;
            $change->setDescription("Preparing to move/rename category '$sourceId' to '$targetId'")
                   ->save();
            $changeId = $change->getId();
        } else {
            $changeId = $adapter->getBatchId();
        }

        // open the files to be moved for edit, and move them
        // move requires files to be opened for add or edit first
        $p4     = $adapter->getConnection();
        $source = dirname(static::idToFilespec($sourceId, $adapter)) . '/...';
        $target = dirname(static::idToFilespec($targetId, $adapter)) . '/...';
        $p4->run('revert', $source);
        $p4->run('revert', $target);
        $p4->run('sync',   $source);
        $p4->run('edit',   $source);
        $p4->run('move',   array($source, $target));
        $p4->run('reopen', array('-c', $changeId, $source, $target));

        // if we're not in a batch, submit the moves.
        if (!$adapter->inBatch()) {
            $change->setDescription("Move/rename category '$sourceId' to '$targetId'")
                   ->submit();
        }
    }
P4Cms_Categorization_CategoryAbstract::save ( description = null)

Extend save to verify that category ancestry exists.

Parameters:
string$descriptionoptional - a description of the change.
Returns:
P4Cms_Record provides a fluent interface
    {
        $this->_checkIdSet('save');

        // verify existence of parentage.
        $adapter = $this->getAdapter();
        $parent  = $this->getParentId();
        while ($parent) {
            if (!static::exists($parent, null, $adapter)) {
                throw new InvalidArgumentException(
                    'Cannot create new category; category ancestry does not exist.'
                );
            }
            $parent = strstr($parent, '/') ? dirname($parent) : null;
        }

        return parent::save($description);
    }
P4Cms_Categorization_CategoryAbstract::setDescription ( description)

Update the description with a new value.

Parameters:
string$descriptionThe new description
Returns:
P4Cms_Category To maintain a fluent interface
    {
        return $this->_setValue('description', $description);
    }
static P4Cms_Categorization_CategoryAbstract::setEntryCategories ( item,
newCategoryIds,
P4Cms_Record_Adapter adapter = null 
) [static]

Set the categories that an entry resides in.

Parameters:
mixed$itemAn id, or a known data structure, to associate with the list of categories.
array$newCategoryIdsA list of category ids to associate with the $item.
P4Cms_Record_Adapter$adapteroptional - storage adapter to use.
Exceptions:
InvalidArgumentException
    {
        $tempCategory = new static;
        if (!is_string($item) || !$tempCategory->_canAcceptId($item)) {
            throw new InvalidArgumentException(
                'Cannot set categories; the entry must either be a string or known data structure.'
            );
        }
        if (!is_array($newCategoryIds)) {
            throw new InvalidArgumentException(
                'Cannot set categories; categories must be an array.'
            );
        }

        // if no adapter given, use default.
        $adapter = $adapter ?: static::getDefaultAdapter();

        // ensure that the new categories exist
        foreach ($newCategoryIds as $id) {
            if (!static::exists($id)) {
                throw new P4Cms_Categorization_Exception(
                    "Cannot add entry to a non-existant category."
                );
            }
        }

        // now fetch the current categories
        $categoryIds = static::fetchIdsByEntry($item, $adapter);

        // compute the difference between the two lists.
        list($adds, $deletes) = P4Cms_ArrayUtility::computeDiff($categoryIds, $newCategoryIds);

        // add $item to these categories
        foreach ($adds as $id) {
            $category = new static;
            $category->setId($id)->addEntry($item);
        }

        // remove $item from these categories
        foreach ($deletes as $id) {
            $category = new static;
            $category->setId($id)->deleteEntry($item);
        }
    }
P4Cms_Categorization_CategoryAbstract::setId ( id)

Set the id for this category category.

Parameters:
null | string$idthe id for this record - null to clear.
Returns:
P4Cms_Categorization_CategoryAbstract provides fluent interface.
Exceptions:
InvalidArgumentExceptionif $id contains '/' when nesting is not allowed.
InvalidArgumentExceptionif $id contains invalid characters.

Reimplemented from P4Cms_Record.

    {
        $validator = new P4Cms_Validate_CategoryId;
        if ($id !== null && !$validator->isValid($id, static::CATEGORY_FILENAME)) {
            $messages = array_values($validator->getMessages());
            throw new InvalidArgumentException(
                "Cannot set id: ". $messages[0]
            );
        }

        if (strpos($id, '/') !== false) {
            static::_checkNestability();
        }

        return parent::setId($id);
    }
P4Cms_Categorization_CategoryAbstract::setTitle ( title)

Update the title with a new value.

Parameters:
string$titleThe new title
Returns:
P4Cms_Category To maintain a fluent interface
    {
        return $this->_setValue('title', $title);
    }

Member Data Documentation

P4Cms_Categorization_CategoryAbstract::$_fields [static, protected]
Initial value:
 array(
        'title'         => array(
            'accessor'  => 'getTitle',
            'mutator'   => 'setTitle'
        ),
        'description'   => array(
            'accessor'  => 'getDescription',
            'mutator'   => 'setDescription'
        )
    )

Specify the fields for category metadata.

Reimplemented from P4Cms_Record.

Reimplemented in Category_Model_Category.

P4Cms_Categorization_CategoryAbstract::$_nestingAllowed = false [static, protected]

Specifies whether the concrete category allows nested categories.

Reimplemented in Category_Model_Category.

P4Cms_Categorization_CategoryAbstract::$_storageSubPath = null [static, protected]

Specifies the sub-path to use for storage of category data.

This is used in combination with the records path to construct the full storage path. The implementing class MUST set this property.

Reimplemented from P4Cms_Record.

Reimplemented in Category_Model_Category.

Defines the filename to contain category metadata.

Defines the prefix for encoded category entry ids.


The documentation for this class was generated from the following file: