netglue/prismic-php-kit

View on GitHub
src/Prismic/Document.php

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
<?php
declare(strict_types=1);

namespace Prismic;

use DateTime;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use Prismic\Document\Fragment\FragmentCollection;
use Prismic\Document\Fragment\FragmentInterface;
use Prismic\Document\Fragment\Link\DocumentLink;
use stdClass;

class Document implements DocumentInterface
{

    /** @var string */
    protected $id;

    /**
     * @var string|null
     */
    protected $uid;

    /**
     * @var string
     */
    protected $type;

    /**
     * @var array
     */
    protected $tags;

    /** @var array */
    protected $slugs;

    /**
     * @var DateTimeInterface|null
     */
    protected $firstPublished;

    /**
     * @var DateTimeInterface|null
     */
    protected $lastPublished;

    /**
     * @var string|null
     */
    protected $lang;

    /**
     * @var string
     */
    protected $href;

    /**
     * An array of Document Link pointing to translations of this document
     *
     * @var array
     */
    protected $alternateLanguages;

    /**
     * @var FragmentCollection
     */
    protected $data;

    /**
     * @var Api
     */
    protected $api;

    private function __construct()
    {
    }

    public static function fromJsonString(string $json, Api $api) : DocumentInterface
    {
        $data = \json_decode($json);
        if (! $data) {
            throw new Exception\InvalidArgumentException(sprintf(
                'Failed to decode json payload: %s',
                \json_last_error_msg()
            ), \json_last_error());
        }
        return static::fromJsonObject($data, $api);
    }

    public static function fromJsonObject(stdClass $data, Api $api) : DocumentInterface
    {
        $inst        = new static;
        $inst->api   = $api;
        $inst->id    = $inst->assertRequiredProperty($data, 'id', false);
        $inst->uid   = $inst->assertRequiredProperty($data, 'uid', true);
        $inst->type  = $inst->assertRequiredProperty($data, 'type', false);
        $inst->tags  = $inst->assertRequiredProperty($data, 'tags', false);
        $inst->lang  = $inst->assertRequiredProperty($data, 'lang', true);
        $inst->href  = $inst->assertRequiredProperty($data, 'href', false);
        $inst->slugs = $inst->assertRequiredProperty($data, 'slugs', false);

        $utc            = new DateTimeZone('UTC');
        $firstPublished = $inst->assertRequiredProperty($data, 'first_publication_date', true);
        if ($firstPublished) {
            $date                 = DateTimeImmutable::createFromFormat(DateTime::ISO8601, $firstPublished);
            $inst->firstPublished = $date->setTimezone($utc);
        }
        $lastPublished = $inst->assertRequiredProperty($data, 'last_publication_date', true);
        if ($lastPublished) {
            $date                = DateTimeImmutable::createFromFormat(DateTime::ISO8601, $lastPublished);
            $inst->lastPublished = $date->setTimezone($utc);
        }

        $altLang                  = $inst->assertRequiredProperty($data, 'alternate_languages', true);
        $inst->alternateLanguages = $altLang ? $altLang : [];

        $data = $inst->assertRequiredProperty($data, 'data', false);

        /**
         * The root node in the data property is prefixed with the document type in the V1 API
         */
        $data = $api->isV1Api()
            ? $data->{$inst->type}
            : $data;

        if (! $api->getLinkResolver()) {
            throw new Exception\RuntimeException(
                'Documents cannot be properly hydrated without a Link Resolver being made available to the API'
            );
        }

        $inst->data = FragmentCollection::factory($data, $api->getLinkResolver());


        return $inst;
    }

    protected function assertRequiredProperty(stdClass $object, string $property, $nullable = true)
    {
        if (! \property_exists($object, $property)) {
            throw new Exception\InvalidArgumentException(sprintf(
                'A required document property was missing from the JSON payload: %s',
                $property
            ));
        }
        $value = $object->{$property};
        if (null === $value && false === $nullable) {
            throw new Exception\InvalidArgumentException(sprintf(
                'A required document property was found to be null in the JSON payload: %s',
                $property
            ));
        }
        return $object->{$property};
    }

    public function getId() : string
    {
        return $this->id;
    }

    public function getUid() : ?string
    {
        return $this->uid;
    }

    public function getType() : string
    {
        return $this->type;
    }

    public function getTags() : array
    {
        return $this->tags;
    }

    public function getSlugs() : array
    {
        return $this->slugs;
    }

    public function getSlug() :? string
    {
        return count($this->slugs) ? current($this->slugs) : null;
    }

    public function getFirstPublicationDate() : ?DateTimeInterface
    {
        return $this->firstPublished;
    }

    public function getLastPublicationDate() : ?DateTimeInterface
    {
        return $this->lastPublished;
    }

    public function getLang() : ?string
    {
        return $this->lang;
    }

    public function getHref() : string
    {
        return $this->href;
    }

    public function getAlternateLanguages() : array
    {
        return $this->alternateLanguages;
    }

    /**
     * Return a translation of this document for the given language if one exists
     * @param string $lang
     * @return null|DocumentInterface
     * @throws Exception\ExceptionInterface If a matching document is found but an error occurs retrieving it
     */
    public function getTranslation(string $lang) :? DocumentInterface
    {
        foreach ($this->alternateLanguages as $language) {
            if (isset($language->lang) && $language->lang === $lang) {
                $id = isset($language->id) ? (string) $language->id : null;
                return $id ? $this->api->getById($id) : null;
            }
        }
        return null;
    }

    public function getData() : FragmentCollection
    {
        return $this->data;
    }

    public function get(string $key) :? FragmentInterface
    {
        return $this->data->get($key);
    }

    public function has(string $key) : bool
    {
        return $this->data->has($key);
    }

    public function asLink() : DocumentLink
    {
        /** @var LinkResolver $resolver */
        $resolver = $this->api->getLinkResolver();
        return DocumentLink::withDocument($this, $resolver);
    }
}