d3yii2/d3files

View on GitHub
models/D3files.php

Summary

Maintainability
C
1 day
Test Coverage
<?php

namespace d3yii2\d3files\models;

use d3system\exceptions\D3ActiveRecordException;
use d3system\exceptions\D3Exception;
use d3yii2\d3files\components\FileHandler;
use ReflectionException;
use Yii;
use yii\db\ActiveQuery;
use yii\db\ActiveRecord;
use yii\db\Exception;
use yii\db\Expression;
use yii\web\ForbiddenHttpException;
use yii\web\UploadedFile;

/**
 * This is the model class for table "d3files".
 *
 * @property string $id
 * @property integer $type_id
 * @property string $file_name
 * @property string $add_datetime
 * @property integer $user_id
 * @property string $notes
 *
 * @property D3filesModel[] $d3filesModels
 */
class D3files extends ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName(): string
    {
        return 'd3files';
    }

    /**
     * alternative for uploading file
     *
     * @param string $fileName
     * @param string $modelName
     * @param int $modelId
     * @param string $filePath
     * @param string $fileTypes
     * @param int|null $userId
     * @throws \ReflectionException
     * @throws \d3system\exceptions\D3ActiveRecordException
     * @throws \d3system\exceptions\D3Exception
     * @throws \yii\base\Exception
     */
    public static function saveFile(string $fileName,string  $modelName,int $modelId,string $filePath,string $fileTypes,?int $userId = 0): void
    {
        $fileHandler = new FileHandler(
            [
                'model_name' => $modelName,
                'model_id' => uniqid('d3files', false),
                'file_name' => $fileName,
                'file_types' => $fileTypes,
                'file_path' => $filePath,
            ]
        );

        $model = new self();

        $model->file_name = $fileName;
        $model->add_datetime = new Expression('NOW()');
        $model->user_id = $userId;

        if (!$model->save()) {
            $fileHandler->remove();
            throw new D3Exception(Yii::t('d3files', 'Insert DB record failed'));
        }
    
        self::saveModelName($modelName, $modelId, $model->id);
        $fileHandler->rename($model->id);
    }

    /**
     * alternative for uploading file
     *
     * @param string $fileName
     * @param string $modelName
     * @param int $modelId
     * @param string $fileContent
     * @param string $fileTypes
     * @param int|null $userId
     * @throws \ReflectionException
     * @throws \d3system\exceptions\D3ActiveRecordException
     * @throws \d3system\exceptions\D3Exception
     * @throws \yii\base\Exception
     * @throws \yii\db\Exception
     */
    public static function saveContent(string $fileName,string  $modelName,int $modelId,string $fileContent,string $fileTypes,?int $userId = 0): void
    {
        $fileHandler = new FileHandler(
            [
                'file_name' => $fileName,
                'model_name' => $modelName,
                'model_id' => $modelId,
                'file_types' => $fileTypes,
            ]
        );

        $model = new self();

        $model->file_name = $fileName;
        $model->add_datetime = new Expression('NOW()');
        $model->user_id = $userId;

        if ($model->save()) {
            self::saveModelName($modelName, $modelId, $model->id);
            $fileHandler->setModelId($model->id);
            
            if (!$fileHandler->save($fileContent)) {
                throw new D3Exception('D3Files: Cannot save the file: ' . $fileName);
            }
        } else {
            $fileHandler->remove();
            throw new Exception(Yii::t('d3files', 'Insert DB record failed'));
        }
    }

    /**
     * Upload yii\web\UploadedFile
     * @param UploadedFile $uploadFile
     * @param string $modelName model name with name space
     * @param int $modelId
     * @throws \Exception
     */
    public static function saveYii2UploadFile(UploadedFile $uploadFile, string $modelName,int $modelId): void
    {
        $fileHandler = new FileHandler(
            [
                'model_name' => $modelName,
                'model_id' => uniqid('d3f', false),
                'file_name' => $uploadFile->name,
                'file_types' => '*', //yii2 model control file types
            ]
        );

        $fileHandler->uploadYii2UloadFile($uploadFile);

        $model = new self();

        $model->file_name = $uploadFile->name;
        $model->add_datetime = new Expression('NOW()');
        $model->user_id = Yii::$app->person->user_id;

        if ($model->save()) {
            self::saveModelName($modelName, $modelId, $model->id);
            $fileHandler->rename($model->id);
        } else {
            $fileHandler->remove();
            throw new D3ActiveRecordException($model, Yii::t('d3files', 'Insert DB record failed'));
        }
    }

    /**
     * @param string $modelName
     * @param int $modelId
     * @param int $filesModelId
     * @throws \d3system\exceptions\D3ActiveRecordException
     */
    private static function saveModelName(string $modelName, int $modelId, int $filesModelId): void
    {
        // Get or create model name id
        $nameModel = new D3filesModelName();
        $model_name_id = $nameModel->getByName($modelName, true);

        $filesModel = new D3filesModel();
        $filesModel->d3files_id = $filesModelId;
        $filesModel->is_file = 1;
        $filesModel->model_name_id = $model_name_id;
        $filesModel->model_id = $modelId;
        if (!$filesModel->save()) {
            throw new D3ActiveRecordException($filesModel, null, 'Cannot save D3filesModel');
        }
    }

    /**
     * get file list with file_path
     *
     * @param string $modelName model class name
     * @param int $modelId   model record id
     * @return array [
     *                   'id' => 88,                          //d3files.id
     *                   'file_name' => 'picture.jpg',        //d3files.file_name
     *                   'file_model_id' => '44'              //d3files_model.id
     *                   'file_path => '/var/www/car/upload/car/111.jpg'
     *               ]
     * @throws ReflectionException
     */
    public static function getRecordFilesList(string $modelName, int $modelId): array
    {
        $filesList = self::fileListForWidget($modelName, $modelId);
        foreach ($filesList as $k => $fileRow) {
            $fileHandler = new FileHandler(
                [
                    'model_name' => $modelName,
                    'model_id' => $fileRow['id'],
                    'file_name' => $fileRow['file_name'],
                ]
            );
            $filesList[$k]['file_path'] = $fileHandler->getFilePath();
        }

        return $filesList;
    }

    /**
     * get file list for widget
     *
     * @param string $modelName
     * @param int $modelId
     * @param bool $forPath get data for building file path
     * @return array
     * @throws \yii\db\Exception
     */
    public static function fileListForWidget(
        string $modelName,
        int $modelId,
        bool $forPath = false
    ): array
    {
        if ($forPath) {
            $sSql = '
            SELECT 
              f.id,
              f.file_name,
              f.notes,
              CASE fm.is_file 
                WHEN 1 THEN fm.id 
                ELSE fmBase.id
              END file_model_id,
              CASE fm.is_file 
                WHEN 1 THEN fmn.name
                ELSE fmnBase.name
              END className              
            FROM
              d3files f
              INNER JOIN d3files_model fm
                ON f.id = fm.d3files_id
              INNER JOIN d3files_model_name fmn
                ON fm.model_name_id = fmn.id
              LEFT OUTER JOIN d3files_model fmBase
                ON fm.is_file = 0
                  AND fm.d3files_id = fmBase.d3files_id
                  AND fmBase.is_file = 1   
              LEFT OUTER JOIN d3files_model_name fmnBase
                ON fmnBase.id = fmBase.model_name_id    
            WHERE fmn.name    = :model_name
              AND fm.model_id = :model_id
              AND fm.deleted  = 0
        ';

        } else {
            $sSql = '
            SELECT 
              f.id,
              f.file_name,
              f.notes,
              fm.id  file_model_id
            FROM
              d3files f
              INNER JOIN d3files_model fm
                ON f.id = fm.d3files_id
              INNER JOIN d3files_model_name fmn
                ON fm.model_name_id = fmn.id
            WHERE fmn.name    = :model_name
              AND fm.model_id = :model_id
              AND fm.deleted  = 0
        ';
        }

        return Yii::$app
            ->getDb()
            ->createCommand($sSql,[
                ':model_name' => $modelName,
                ':model_id' => $modelId,
            ])
            ->queryAll();
    }

    /**
     * get file list for widget by model name id and model id
     *
     * @param int $modelNameId
     * @param int $modelId
     * @return array
     */
    public static function fileListForWidgetByNameId(int $modelNameId, int $modelId): array
    {
        $sSql = /** @lang text */
            '
            SELECT 
              f.id,
              f.file_name,
              fm.id  file_model_id
            FROM
              d3files f
              INNER JOIN d3files_model fm
                ON f.id = fm.d3files_id
              INNER JOIN d3files_model_name fmn
                ON fm.model_name_id = fmn.id
            WHERE fmn.name_id    = :model_name_id
              AND fm.model_id = :model_id
              AND fm.deleted  = 0
        ';

        $parameters = [
            ':model_name_id' => $modelNameId,
            ':model_id' => $modelId,
        ];

        $connection = Yii::$app->getDb();
        $command = $connection->createCommand($sSql, $parameters);
        return $command->queryAll();
    }

    /**
     * @param string $model_name
     * @param int $model_id
     * @throws ForbiddenHttpException
     */
    public static function performReadValidation(string $model_name, int $model_id): void
    {
        /** @var ActiveRecord $model_name */
        $modelMain = $model_name::findOne($model_id);
        if (!$modelMain) {
            throw new ForbiddenHttpException(Yii::t('d3files', "You don't have access to parent record"));
        }
    }

    /**
     * @param string $modelClass
     * @param array $modelIds
     * @return array
     */

    public static function forListBox(string $modelClass, array $modelIds): array
    {
        $records = self::getAllByModelRecordIds($modelClass, $modelIds);

        $items = [];

        foreach ($records as $record) {
            $fileExt = (string) pathinfo($record['file_name'], PATHINFO_EXTENSION);

            if (isset($items[$fileExt])) {
                continue;
            }
            $items[$fileExt] = $fileExt;
        }

        return $items;
    }

    /**
     * @param string $modelClass
     * @param array $ids
     * @return array
     */
    public static function getAllByModelRecordIds(string $modelClass, array $ids): array
    {
        if (empty($ids)) {
            return [];
        }

        // Sanitization
        $array_map = [];
        foreach ($ids as $key => $id) {
            $array_map[$key] = (int)$id;
        }
        $ids = $array_map;

        $sSql = /** @lang text */
            '
            SELECT 
              f.id,
              f.file_name,
              fm.id  file_model_id,
              fm.model_id
            FROM
              d3files f
              INNER JOIN d3files_model fm
                ON f.id = fm.d3files_id
              INNER JOIN d3files_model_name fmn
                ON fm.model_name_id = fmn.id
            WHERE fm.model_id IN (' . implode(',', $ids) . ')
              AND fmn.name    = :model_name
              AND fm.deleted  = 0
            ORDER BY file_model_id
        ';

        $parameters = [
            ':model_name' => $modelClass,
        ];

        $connection = Yii::$app->getDb();
        $command = $connection->createCommand($sSql, $parameters);
        return $command->queryAll();
    }

    /**
     * @return array
     */
    public function rules(): array
    {
        return [
            [['type_id', 'user_id'], 'integer'],
            [['file_name', 'add_datetime', 'user_id'], 'required'],
            [['add_datetime'], 'safe'],
            [['notes'], 'string'],
            [['file_name'], 'string', 'max' => 255],
        ];
    }

    /**
     * @return array
     */
    public function attributeLabels(): array
    {
        return [
            'id' => Yii::t('d3files', 'ID'),
            'type_id' => Yii::t('d3files', 'Type ID'),
            'file_name' => Yii::t('d3files', 'File Name'),
            'add_datetime' => Yii::t('d3files', 'Add Datetime'),
            'user_id' => Yii::t('d3files', 'User ID'),
            'notes' => Yii::t('d3files', 'Notes'),
        ];
    }

    /**
     * @return ActiveQuery
     */
    public function getD3filesModels(): ActiveQuery
    {
        return $this->hasMany(D3filesModel::class, ['d3files_id' => 'id']);
    }
}