AJenbo/agcms

View on GitHub
application/inc/Models/Page.php

Summary

Maintainability
C
1 day
Test Coverage
C
77%
<?php

namespace App\Models;

use App\Exceptions\Exception;
use App\Services\DbService;
use App\Services\OrmService;

class Page extends AbstractRenderable implements InterfaceRichText
{
    use HasIcon;

    /** Table name in database. */
    public const TABLE_NAME = 'sider';

    // Backed by DB

    /** @var string Stock keeping unit. */
    private string $sku = '';

    /** @var int Latest save time. */
    private int $timeStamp;

    /** @var string Page keywords, coma seporated. */
    private string $keywords = '';

    /** @var string HTML body. */
    private string $html = '';

    /** @var string Short text description. */
    private string $excerpt = '';

    /** @var ?int Id of requirement page. */
    private ?int $requirementId;

    /** @var ?int Id of brand. */
    private ?int $brandId;

    /** @var int Current price. */
    private int $price = 0;

    /** @var int Previous price. */
    private int $oldPrice = 0;

    /** @var int What type of price is the current (from, specific). */
    private int $priceType = 0;

    /** @var int What type of price is the previous (from, specific). */
    private int $oldPriceType = 0;

    public function __construct(array $data = [])
    {
        $this->iconId = intOrNull($data['icon_id'] ?? null);
        $this->setSku(strval($data['sku']))
            ->setTimeStamp(intval($data['timestamp'] ?? 0))
            ->setKeywords(strval($data['keywords']))
            ->setExcerpt(strval($data['excerpt']))
            ->setRequirementId(intOrNull($data['requirement_id']))
            ->setBrandId(intOrNull($data['brand_id']))
            ->setPrice(intval($data['price']))
            ->setOldPrice(intval($data['old_price']))
            ->setPriceType(intval($data['price_type']))
            ->setOldPriceType(intval($data['old_price_type']))
            ->setHtml(strval($data['html']))
            ->setTitle(strval($data['title']))
            ->setId(intOrNull($data['id'] ?? null));
    }

    public static function mapFromDB(array $data): array
    {
        return [
            'id'             => $data['id'],
            'sku'            => $data['varenr'],
            'timestamp'      => strtotime($data['dato']) + app(DbService::class)->getTimeOffset(),
            'title'          => $data['navn'],
            'keywords'       => $data['keywords'],
            'html'           => $data['text'],
            'excerpt'        => $data['beskrivelse'],
            'icon_id'        => $data['icon_id'],
            'requirement_id' => $data['krav'],
            'brand_id'       => $data['maerke'],
            'price'          => $data['pris'],
            'old_price'      => $data['for'],
            'price_type'     => $data['fra'],
            'old_price_type' => $data['burde'],
        ];
    }

    /**
     * Delete page and it's relations.
     */
    public function delete(): bool
    {
        $db = app(DbService::class);

        // Forget affected tables, though alter indivitual deletes will forget most
        $db->addLoadedTable('list_rows');
        $db->query('DELETE FROM `list_rows` WHERE `link` = ' . $this->getId());
        foreach ($this->getTables() as $table) {
            $table->delete();
        }

        // parent::delete will forget any binding and accessory relationship
        $db->addLoadedTable('bind');
        $db->query('DELETE FROM `bind` WHERE side = ' . $this->getId());
        $db->addLoadedTable('tilbehor');
        $db->query('DELETE FROM `tilbehor` WHERE side = ' . $this->getId() . ' OR tilbehor =' . $this->getId());

        return parent::delete();
    }

    // Getters and setters

    /**
     * Set the Stock Keeping Unit identifyer.
     *
     * @return $this
     */
    public function setSku(string $sku): self
    {
        $this->sku = $sku;

        return $this;
    }

    /**
     * Get the Stock Keeping Unity.
     */
    public function getSku(): string
    {
        return $this->sku;
    }

    /**
     * Set the last modefied time stamp.
     *
     * @return $this
     */
    public function setTimeStamp(int $timeStamp): self
    {
        $this->timeStamp = $timeStamp;

        return $this;
    }

    /**
     * Get last modefied.
     */
    public function getTimeStamp(): int
    {
        return $this->timeStamp;
    }

    /**
     * @param string $keywords Comma seporated
     *
     * @return $this
     */
    public function setKeywords(string $keywords): self
    {
        $this->keywords = $keywords;

        return $this;
    }

    public function getKeywords(): string
    {
        return $this->keywords;
    }

    /**
     * @return $this
     */
    public function setHtml(string $html): InterfaceRichText
    {
        $this->html = $html;

        return $this;
    }

    /**
     * Get the HTML body.
     */
    public function getHtml(): string
    {
        return $this->html;
    }

    /**
     * Set the breaf description.
     *
     * @return $this
     */
    public function setExcerpt(string $excerpt): self
    {
        $this->excerpt = $excerpt;

        return $this;
    }

    /**
     * Get the short description.
     */
    public function getExcerpt(): string
    {
        if (!$this->excerpt) {
            $excerpt = preg_replace(['/</', '/>/', '/\s+/'], [' <', '> ', ' '], $this->html);
            if (null === $excerpt) {
                throw new Exception('preg_replace failed');
            }
            $excerpt = strip_tags($excerpt);
            $excerpt = preg_replace('/\s+/', ' ', $excerpt);
            if (null === $excerpt) {
                throw new Exception('preg_replace failed');
            }

            return stringLimit($excerpt, 100);
        }

        return $this->excerpt;
    }

    /**
     * Check if an except has been entered manually.
     */
    public function hasExcerpt(): bool
    {
        return (bool)$this->excerpt;
    }

    /**
     * @return $this
     */
    public function setRequirementId(?int $requirementId): self
    {
        $this->requirementId = $requirementId;

        return $this;
    }

    /**
     * @return $this
     */
    public function setBrandId(?int $brandId): self
    {
        $this->brandId = $brandId;

        return $this;
    }

    /**
     * Set the price.
     *
     * @return $this
     */
    public function setPrice(int $price): self
    {
        $this->price = $price;

        return $this;
    }

    /**
     * Get the price.
     */
    public function getPrice(): int
    {
        return $this->price;
    }

    /**
     * Set the old price.
     *
     * @param int $oldPrice The previous price
     *
     * @return $this
     */
    public function setOldPrice(int $oldPrice): self
    {
        $this->oldPrice = $oldPrice;

        return $this;
    }

    /**
     * Get the previous price.
     */
    public function getOldPrice(): int
    {
        return $this->oldPrice;
    }

    /**
     * Set the price type.
     *
     * @return $this
     */
    public function setPriceType(int $priceType): self
    {
        $this->priceType = $priceType;

        return $this;
    }

    /**
     * Get the price Type.
     */
    public function getPriceType(): int
    {
        return $this->priceType;
    }

    /**
     * Set the type of the privious price.
     *
     * @return $this
     */
    public function setOldPriceType(int $oldPriceType): self
    {
        $this->oldPriceType = $oldPriceType;

        return $this;
    }

    /**
     * Get the previous price Type.
     */
    public function getOldPriceType(): int
    {
        return $this->oldPriceType;
    }

    // General methods

    /**
     * Get the url slug.
     */
    public function getSlug(): string
    {
        return 'side' . $this->getId() . '-' . cleanFileName($this->getTitle()) . '.html';
    }

    /**
     * Get canonical url for this entity.
     *
     * @param null|Category $category Category to base the url on
     */
    public function getCanonicalLink(?Category $category = null): string
    {
        $url = '/';
        if (!$category || !$this->isInCategory($category)) {
            $category = $this->getPrimaryCategory();
        }
        if ($category) {
            $url = $category->getCanonicalLink();
        }

        return $url . $this->getSlug();
    }

    /**
     * Check if the page i attached to a given category.
     */
    public function isInCategory(Category $category): bool
    {
        $db = app(DbService::class);

        $db->addLoadedTable('bind');

        return (bool)$db->fetchOne(
            '
            SELECT kat FROM `bind`
            WHERE side = ' . $this->getId() . '
            AND kat = ' . $category->getId()
        );
    }

    /**
     * Get the primery category for this page.
     *
     * @return ?Category
     */
    public function getPrimaryCategory(): ?Category
    {
        return app(OrmService::class)->getOneByQuery(Category::class, $this->getCategoriesQuery());
    }

    /**
     * Get all categories.
     *
     * @return Category[]
     */
    public function getCategories(): array
    {
        return app(OrmService::class)->getByQuery(Category::class, $this->getCategoriesQuery());
    }

    /**
     * Generate the query for getting all categories where this page is linke.
     */
    private function getCategoriesQuery(): string
    {
        app(DbService::class)->addLoadedTable('bind');

        return 'SELECT * FROM `kat` WHERE id IN (SELECT kat FROM `bind` WHERE side = ' . $this->getId() . ')';
    }

    /**
     * Add the page to a given category.
     */
    public function addToCategory(Category $category): void
    {
        app(DbService::class)->query(
            'INSERT INTO `bind` (`side`, `kat`) VALUES (' . $this->getId() . ', ' . $category->getId() . ')'
        );
        app(OrmService::class)->forgetByQuery(self::class, $this->getCategoriesQuery());
    }

    /**
     * Remove the page form a given cateogory.
     */
    public function removeFromCategory(Category $category): void
    {
        app(DbService::class)->query('DELETE FROM `bind` WHERE `side` = ' . $this->getId() . ' AND `kat` = ' . $category->getId());
        app(OrmService::class)->forgetByQuery(self::class, $this->getCategoriesQuery());
    }

    /**
     * Add a page as an accessory.
     */
    public function addAccessory(self $accessory): void
    {
        app(DbService::class)->query(
            '
            INSERT IGNORE INTO `tilbehor` (`side`, `tilbehor`)
            VALUES (' . $this->getId() . ', ' . $accessory->getId() . ')'
        );

        app(OrmService::class)->forgetByQuery(self::class, $this->getAccessoryQuery());
    }

    /**
     * Remove an accessory from the page.
     */
    public function removeAccessory(self $accessory): void
    {
        app(DbService::class)->query(
            'DELETE FROM `tilbehor` WHERE side = ' . $this->getId() . ' AND tilbehor = ' . $accessory->getId()
        );

        app(OrmService::class)->forgetByQuery(self::class, $this->getAccessoryQuery());
    }

    /**
     * Get accessory pages.
     *
     * @return Page[]
     */
    public function getAccessories(): array
    {
        return app(OrmService::class)->getByQuery(self::class, $this->getAccessoryQuery());
    }

    /**
     * Get presentable accessories.
     *
     * @return Page[]
     */
    public function getActiveAccessories(): array
    {
        $accessories = [];
        foreach ($this->getAccessories() as $accessory) {
            if (!$accessory->isInactive()) {
                $accessories[] = $accessory;
            }
        }

        return $accessories;
    }

    /**
     * Get query for finding accessories.
     */
    private function getAccessoryQuery(): string
    {
        app(DbService::class)->addLoadedTable('tilbehor');

        return '
            SELECT * FROM sider
            WHERE id IN (SELECT tilbehor FROM tilbehor WHERE side = ' . $this->getId() . ') ORDER BY navn ASC';
    }

    /**
     * Get tabels.
     *
     * @return Table[]
     */
    public function getTables(): array
    {
        return app(OrmService::class)->getByQuery(
            Table::class,
            'SELECT * FROM `lists` WHERE page_id = ' . $this->getId()
        );
    }

    /**
     * Check if there is a product table attached to this page.
     */
    public function hasProductTable(): bool
    {
        foreach ($this->getTables() as $table) {
            if ($table->hasPrices()) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get product brand.
     *
     * @return ?Brand
     */
    public function getBrand(): ?Brand
    {
        $brand = null;
        if (null !== $this->brandId) {
            $brand = app(OrmService::class)->getOne(Brand::class, $this->brandId);
        }

        return $brand;
    }

    /**
     * Get product requirement.
     *
     * @return ?Requirement
     */
    public function getRequirement(): ?Requirement
    {
        $requirement = null;
        if (null !== $this->requirementId) {
            $requirement = app(OrmService::class)->getOne(Requirement::class, $this->requirementId);
        }

        return $requirement;
    }

    /**
     * Is the product not on the website.
     */
    public function isInactive(): bool
    {
        $category = $this->getPrimaryCategory();
        if ($category) {
            return $category->isInactive();
        }

        return true;
    }

    // ORM related functions

    public function getDbArray(): array
    {
        $this->setTimeStamp(time());

        $db = app(DbService::class);

        return [
            'dato'        => $db->getNowValue(),
            'navn'        => $db->quote($this->title),
            'keywords'    => $db->quote($this->keywords),
            'text'        => $db->quote($this->html),
            'varenr'      => $db->quote($this->sku),
            'beskrivelse' => $db->quote($this->excerpt),
            'icon_id'     => null !== $this->iconId ? (string)$this->iconId : 'NULL',
            'krav'        => null !== $this->requirementId ? (string)$this->requirementId : 'NULL',
            'maerke'      => null !== $this->brandId ? (string)$this->brandId : 'NULL',
            'pris'        => (string)$this->price,
            'for'         => (string)$this->oldPrice,
            'fra'         => (string)$this->priceType,
            'burde'       => (string)$this->oldPriceType,
        ];
    }
}