wikimedia/mediawiki-extensions-Wikibase

View on GitHub
repo/includes/Content/ItemContent.php

Summary

Maintainability
A
45 mins
Test Coverage
<?php

namespace Wikibase\Repo\Content;

use BadMethodCallException;
use InvalidArgumentException;
use LogicException;
use MediaWiki\MediaWikiServices;
use MediaWiki\Title\Title;
use Wikibase\DataModel\Entity\EntityRedirect;
use Wikibase\DataModel\Entity\Item;
use Wikibase\Repo\ItemSearchTextGenerator;

/**
 * Content object for articles representing Wikibase items.
 *
 * @license GPL-2.0-or-later
 * @author Daniel Kinzler
 * @author Bene* < benestar.wikimedia@gmail.com >
 *
 * @method \Wikibase\Repo\Content\ItemHandler getContentHandler()
 */
class ItemContent extends EntityContent {

    public const CONTENT_MODEL_ID = 'wikibase-item';

    /**
     * @var EntityHolder|null
     */
    private $itemHolder;

    /**
     * @var EntityRedirect|null
     */
    private $redirect;

    /**
     * @var Title|null Title of the redirect target.
     */
    private $redirectTitle;

    /**
     * Do not use to construct new stuff from outside of this class,
     * use the static newFoobar methods.
     *
     * In other words: treat as protected (which it was, but now cannot
     * be since we derive from Content).
     *
     * @param EntityHolder|null $itemHolder
     * @param EntityRedirect|null $entityRedirect
     * @param Title|null $redirectTitle Title of the redirect target.
     */
    public function __construct(
        EntityHolder $itemHolder = null,
        EntityRedirect $entityRedirect = null,
        Title $redirectTitle = null
    ) {
        parent::__construct( self::CONTENT_MODEL_ID );

        if ( $itemHolder !== null && $entityRedirect !== null ) {
            throw new InvalidArgumentException(
                'Can not contain an Item and be a redirect at the same time'
            );
        }

        if ( $itemHolder !== null && $itemHolder->getEntityType() !== Item::ENTITY_TYPE ) {
            throw new InvalidArgumentException( '$itemHolder must contain a Item entity!' );
        }

        if ( ( $entityRedirect === null ) !== ( $redirectTitle === null ) ) {
            throw new InvalidArgumentException(
                '$entityRedirect and $redirectTitle must both be provided or both be empty.' );
        }

        if ( $redirectTitle !== null
            && $redirectTitle->getContentModel() !== self::CONTENT_MODEL_ID
        ) {
            if ( $redirectTitle->exists() ) {
                throw new InvalidArgumentException(
                    '$redirectTitle must refer to a page with content model '
                    . self::CONTENT_MODEL_ID );
            }
        }

        $this->itemHolder = $itemHolder;
        $this->redirect = $entityRedirect;
        $this->redirectTitle = $redirectTitle;
    }

    /**
     * Create a new ItemContent object for the provided Item.
     *
     * @param Item $item
     *
     * @return self
     */
    public static function newFromItem( Item $item ) {
        return new static( new EntityInstanceHolder( $item ) );
    }

    /**
     * Create a new ItemContent object representing a redirect to the given item ID.
     *
     * @param EntityRedirect $redirect
     * @param Title $redirectTitle Title of the redirect target.
     *
     * @return self
     */
    public static function newFromRedirect( EntityRedirect $redirect, Title $redirectTitle ) {
        return new static( null, $redirect, $redirectTitle );
    }

    protected function getIgnoreKeysForFilters() {
        // FIXME: Refine this after https://phabricator.wikimedia.org/T205254 is complete
        return [
            'language',
            'site',
            'type',
            'hash',
            'id',
        ];
    }

    /**
     * @see Content::getRedirectTarget
     *
     * @return Title|null
     */
    public function getRedirectTarget() {
        return $this->redirectTitle;
    }

    /**
     * @see EntityContent::getEntityRedirect
     *
     * @return null|EntityRedirect
     */
    public function getEntityRedirect() {
        return $this->redirect;
    }

    /**
     * @note This method cannot be called on redirects (targets will never be resolved)
     * @return Item
     */
    public function getItem() {
        $redirect = $this->getRedirectTarget();

        if ( $redirect ) {
            throw new BadMethodCallException( 'Unresolved redirect to [[' . $redirect->getFullText() . ']]' );
        }

        if ( !$this->itemHolder ) {
            throw new LogicException( 'This content object is empty' );
        }

        // @phan-suppress-next-line PhanTypeMismatchReturnSuperType
        return $this->itemHolder->getEntity( Item::class );
    }

    /**
     * @see EntityContent::getEntity
     *
     * @note This method cannot be called on redirects (targets will never be resolved)
     * @return Item
     */
    public function getEntity() {
        return $this->getItem();
    }

    /**
     * @see EntityContent::getEntityHolder
     *
     * @return EntityHolder|null
     */
    public function getEntityHolder() {
        return $this->itemHolder;
    }

    /**
     * @inheritDoc
     */
    public function getTextForSearchIndex() {
        if ( $this->isRedirect() ) {
            return '';
        }

        // TODO: Refactor ItemSearchTextGenerator to share an interface with
        // FingerprintSearchTextGenerator, so we don't have to re-implement getTextForSearchIndex() here.
        $searchTextGenerator = new ItemSearchTextGenerator();
        $text = $searchTextGenerator->generate( $this->getItem() );

        if ( !MediaWikiServices::getInstance()->getHookContainer()
            ->run( 'WikibaseTextForSearchIndex', [ $this, &$text ] )
        ) {
            return '';
        }

        return $text;
    }

    /**
     * @see EntityContent::isEmpty
     *
     * @return bool True if this is not a redirect and the item is empty.
     */
    public function isEmpty() {
        return !$this->isRedirect() && ( !$this->itemHolder || $this->getItem()->isEmpty() );
    }

    /**
     * @see EntityContent::getEntityPageProperties
     *
     * Records the number of statements in the 'wb-claims' key
     * and the number of sitelinks in the 'wb-sitelinks' key.
     *
     * @return array A map from property names to property values.
     */
    public function getEntityPageProperties() {
        $properties = parent::getEntityPageProperties();

        if ( !$this->isRedirect() ) {
            $item = $this->getItem();
            $properties['wb-claims'] = $item->getStatements()->count();
            $properties['wb-sitelinks'] = $item->getSiteLinkList()->count();
            $properties['wb-identifiers'] = $this->getContentHandler()
                ->getIdentifiersCount( $item->getStatements() );
        }

        return $properties;
    }
}