AJenbo/agcms

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

Summary

Maintainability
A
2 hrs
Test Coverage
F
41%
<?php

namespace App\Models;

use App\Exceptions\Exception;
use App\Exceptions\InvalidInput;
use App\Services\DbService;
use App\Services\OrmService;
use Symfony\Component\Mime\MimeTypes;

class File extends AbstractEntity
{
    /** Table name in database. */
    public const TABLE_NAME = 'files';

    // Backed by DB

    /** @var string File path. */
    private string $path;

    /** @var string File mime. */
    private string $mime;

    /** @var int File byte size. */
    private int $size;

    /** @var string Text description of file. */
    private string $description = '';

    /** @var int Object width in px. */
    private int $width = 0;

    /** @var int Object height in px. */
    private int $height = 0;

    public function __construct(array $data = [])
    {
        $this->setPath(strval($data['path']))
            ->setMime(strval($data['mime']))
            ->setSize(intval($data['size']))
            ->setDescription(strval($data['description']))
            ->setWidth(intval($data['width']))
            ->setHeight(intval($data['height']))
            ->setId(intOrNull($data['id'] ?? null));
    }

    public static function mapFromDB(array $data): array
    {
        return [
            'id'          => $data['id'],
            'path'        => $data['path'],
            'mime'        => $data['mime'],
            'size'        => $data['size'],
            'description' => $data['alt'],
            'width'       => $data['width'],
            'height'      => $data['height'],
        ];
    }

    // Getters and setters

    /**
     * @return $this
     */
    private function setPath(string $path): self
    {
        $this->path = $path;

        return $this;
    }

    public function getPath(): string
    {
        return $this->path;
    }

    /**
     * @return $this
     */
    public function setMime(string $mime): self
    {
        $this->mime = $mime;

        return $this;
    }

    public function getMime(): string
    {
        return $this->mime;
    }

    /**
     * @param int $size The file size in bytes
     *
     * @return $this
     */
    public function setSize(int $size): self
    {
        $this->size = $size;

        return $this;
    }

    /**
     * Get the file size.
     */
    public function getSize(): int
    {
        return $this->size;
    }

    /**
     * @return $this
     */
    public function setDescription(string $description): self
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get the text description.
     */
    public function getDescription(): string
    {
        return $this->description;
    }

    /**
     * Set display width.
     *
     * @return $this
     */
    public function setWidth(int $width): self
    {
        $this->width = $width;

        return $this;
    }

    /**
     * Get display width.
     */
    public function getWidth(): int
    {
        return $this->width;
    }

    /**
     * Set display height.
     *
     * @return $this
     */
    public function setHeight(int $height): self
    {
        $this->height = $height;

        return $this;
    }

    /**
     * Get display height.
     */
    public function getHeight(): int
    {
        return $this->height;
    }

    // ORM related functions

    public function getDbArray(): array
    {
        $db = app(DbService::class);

        return [
            'path'   => $db->quote($this->path),
            'mime'   => $db->quote($this->mime),
            'size'   => (string)$this->size,
            'alt'    => $db->quote($this->description),
            'width'  => (string)$this->width,
            'height' => (string)$this->height,
        ];
    }

    /**
     * Rename file.
     */
    public function move(string $path): bool
    {
        $app = app();

        //Rename/move or give an error
        if (!rename($app->basePath($this->getPath()), $app->basePath($path))) {
            return false;
        }

        $this->replacePaths($this->getPath(), $path);
        $this->setPath($path)->save();

        return true;
    }

    /**
     * Update related data.
     */
    private function replacePaths(string $path, string $newPath): void
    {
        $db = app(DbService::class);

        $newPathEsc = $db->quote('="' . $newPath . '"');
        $pathEsc = $db->quote('="' . $path . '"');
        $db->query('UPDATE sider     SET text = REPLACE(text, ' . $pathEsc . ', ' . $newPathEsc . ')');
        $db->query('UPDATE template  SET text = REPLACE(text, ' . $pathEsc . ', ' . $newPathEsc . ')');
        $db->query('UPDATE special   SET text = REPLACE(text, ' . $pathEsc . ', ' . $newPathEsc . ')');
        $db->query('UPDATE krav      SET text = REPLACE(text, ' . $pathEsc . ', ' . $newPathEsc . ')');
        $db->query('UPDATE newsmails SET text = REPLACE(text, ' . $pathEsc . ', ' . $newPathEsc . ')');
    }

    /**
     * Check if file is in use.
     */
    public function isInUse(bool $onlyCheckHtml = false): bool
    {
        $db = app(DbService::class);

        $escapedPath = $db->quote('%="' . $this->path . '"%');

        $sql = "
              (SELECT id FROM `sider`     WHERE `text` LIKE $escapedPath LIMIT 1)
        UNION (SELECT id FROM `template`  WHERE `text` LIKE $escapedPath LIMIT 1)
        UNION (SELECT id FROM `special`   WHERE `text` LIKE $escapedPath LIMIT 1)
        UNION (SELECT id FROM `krav`      WHERE `text` LIKE $escapedPath LIMIT 1)
        UNION (SELECT id FROM `newsmails` WHERE `text` LIKE $escapedPath LIMIT 1)
        ";
        $db->addLoadedTable('sider', 'template', 'special', 'krav', 'newsmails');

        if (!$onlyCheckHtml) {
            $sql .= '
            UNION (SELECT id FROM `sider`    WHERE `icon_id` = ' . $this->getId() . ' LIMIT 1)
            UNION (SELECT id FROM `template` WHERE `icon_id` = ' . $this->getId() . ' LIMIT 1)
            UNION (SELECT id FROM `maerke`   WHERE `icon_id` = ' . $this->getId() . ' LIMIT 1)
            UNION (SELECT id FROM `kat`      WHERE `icon_id` = ' . $this->getId() . ' LIMIT 1)
            ';
            $db->addLoadedTable('kat');
        }

        return (bool)$db->fetchOne($sql);
    }

    /**
     * Create new File from a file path.
     *
     * @todo Load size of video
     *
     * @return static
     */
    public static function fromPath(string $path): self
    {
        $fullPath = app()->basePath($path);
        $imagesize = @getimagesize($fullPath);
        if (!$imagesize) {
            $imagesize = [];
        }

        $guesser = new MimeTypes();
        $mime = $guesser->guessMimeType($fullPath);

        $file = new static([
            'path'        => $path,
            'mime'        => $mime,
            'size'        => filesize($fullPath),
            'description' => '',
            'width'       => $imagesize[0] ?? 0,
            'height'      => $imagesize[1] ?? 0,
        ]);

        return $file;
    }

    /**
     * Delete entity and file.
     *
     * @throws Exception
     * @throws InvalidInput
     */
    public function delete(): bool
    {
        if ($this->isInUse()) {
            throw new InvalidInput(sprintf(_('"%s" is still in use.'), $this->path), 423);
        }

        $app = app();

        if (file_exists($app->basePath($this->path)) && !unlink($app->basePath($this->path))) {
            throw new Exception(sprintf(_('Could not delete "%s".'), $this->path), 403);
        }

        return parent::delete();
    }

    /**
     * @return ?self
     */
    public static function getByPath(string $path): ?self
    {
        $file = app(OrmService::class)->getOneByQuery(
            static::class,
            'SELECT * FROM `' . self::TABLE_NAME . '` WHERE path = ' . app(DbService::class)->quote($path)
        );

        return $file;
    }
}