Инженерные Решения В Проектах?

Доброе время суток.

С Yii недавно начал общаться, до этого работал с drual 6 - 7. Не доконца понимаю как проектировать приложение(Относительно Yii).

В этом топике хотелось бы обсудить эффективные инженерные решения.

Меня заинтересовало инженерное решение кеширования картинок. Много разных вариантов решения этой маленькой проблемы. Но интересно спросить у гуру как это делается наиболее эффективно для хорошо посещаемых сайтов, скажем от 5000 - 10000 посетителей в день и 10 - 15 активных модераторов…

Собственно у меня используется загрузка изображений к материалу (от 3 и более). Допустим сохраняю оригинал в папку Images(При создании поста, новости и т.д.), посредством вызова beforeSave(Вид пути до оригинала: /images/{Yii::app()->user->id}/coll-{$model->collection->id}/money-{$model->id}/). Имена генерирую с помощью uniqid. Используя допустим компонент "Image" - тут же создаю необходимые для вывода позьзователю изображения с измененным размером(пример пути: /assets/{Yii::app()->user->id}/coll-{$model->collection->id}/money-{$model->id}/{$presetName}/ где $presetName - Скажем размер изображения или понятное название…). $model->collection - это relations к связанной таблице.

Пользователь имеет скажем 3 коллекции в каждой по 1000 монет(таких пользователей может быть много)… Если путь для каждой монеты генерировать, то страница будет долго грузиться, что не допустимо. Как правильно в этой ситуации кешировать? И хранить инфо о файлах. Хранить пути в базе данных(MySQL) не есть гуд… Колво записей будет невероятно много… Есть вариант сканить папку, как это должно взаимодействовать с кешем?

Наиболее интересные решения в процессе дисскусий будут собираться в кучку и добавлятьс в начало поста.

С уважением Михаил!

Например, можно решить средствами nginx.

Мне интересны не серверные изменения, а именно решения или идеи с помощью которых данные вопросы можно решить на уровне Yii. Вообще как правильно распологать те или иные действия конкретной задачи в Yii?

Например мне понравилась идея с сохранением файлов состоящее из:




// Upload.php

class Upload extends CFormModel {


    public function uploadFile($model, $fileName) {

        $dir = "images";

        $file = CUploadedFile::getInstance($model, $fileName);

        if ($file->saveAs($dir . DIRECTORY_SEPARATOR . $file->getName())){

            // Сдесь же можно создавать скажем превьюшьки и дополнительные размеры фоток...

            /*

              $image = Yii::app()->image->load($dir . DIRECTORY_SEPARATOR . $file->getName());

              $image->resize(400, 100);

              $image->save("assets/400x100/");

            */

            return $uploaded = $dir . DIRECTORY_SEPARATOR . $file->getName();

        } else

            return false;

    }

}



Только вот я запутался… у меня все сохраняет… Но там ли я это делаю?

Так это вызывается:




// в модели, с полем images

protected function afterSave(){

    $file = new Upload();

    $file->uploadFile($this, "images");

}



Я бы не генерировал превьюшки сразу, а ставил бы генерацию в очередь.

Можете показать пример? Интересно как это выглядит.

Мои изыскания привели меня к такому решению(не универсально для всего сайта, но полезно для конкретной модели):




// Upload.php

class Upload extends CFormModel {


    public function uploadFile($model, $fieldName) {

        $files = CUploadedFile::getInstancesByName('images');

        if (!is_dir($model->getDir()))

            mkdir($model->getDir(), '0777', true);

        foreach ($files as $file) {

            $ext = pathinfo($file->name, PATHINFO_EXTENSION);

            $fName = uniqid() . "." . $ext;

            $file->saveAs($model->getDir() . "/" . $fName);

        }

    }


    public function delLoadImg($model) {

        $dir = $model->getDir();

        $this->removeDir($dir);


        $dir = 'assets/' . Yii::app()->user->id .

                "/coll-" . $model->collection->id . "/money-" . $model->id;

        $this->removeDir($dir);

    }


    public function removeDir($dir) {

        if ($objs = glob($dir . "/*")) {

            foreach ($objs as $obj) {

                is_dir($obj) ? $this->removeDir($obj) : unlink($obj);

            }

        }

        rmdir($dir);

    }


}






// Часть модели Money.php

//...

protected function afterSave() {

        if ($this->scenario == 'insert' || $this->scenario == 'update') {

            $file = new Upload();

            $file->uploadFile($this, "images");

        }

    }


    public function getDir($preset = '') {

        if (empty($preset))

            return 'images/' . Yii::app()->user->id .

                    "/coll-" . $this->collection->id . "/money-" . $this->id;

        else

            return 'assets/' . Yii::app()->user->id .

                    "/coll-" . $this->collection->id . "/money-" . $this->id . "/" . $preset;

    }


    public function getImg($preset = '') {

        if (empty($preset)) {

            $dir = $this->getDir();

        } else {

            $dir = $this->getDir($preset);

        }

        if (!is_dir($dir))

            $this->createImgCache($preset);

        $d = array();

        $arr = opendir($dir);

        while ($v = readdir($arr)) {

            if ($v == '.' or $v == '..')

                continue;

            if (!is_dir($dir . "/" . $v)) {

                $d[] = "/" . $dir . "/" . $v;

            }

        }


        return $d;

    }


    public function createImgCache($preset = null) {

        if ($preset == null)

            return false;

        $files = $this->getImg();

        $item = Yii::app()->params['preset'][$preset];

        foreach ($files as $file) {

            $image = Yii::app()->image->load(ltrim($file, "/"));

            $image->resize($item['width'], $item['height']);

            if (!is_dir($this->getDir($item['name'])))

                mkdir($this->getDir($item['name']), "0777", true);

            $arrPath = explode("/", $file);

            $fName = array_pop($arrPath);

            $image->save($this->getDir($item['name']) . "/" . $fName); // or $image->save();

        }

    }


    protected function beforeDelete() {

        if (!parent::beforeDelete())

            return false;

        $file = new Upload();

        $file->delLoadImg($this);

        return true;

    }

// ...






// Кусочек conf.php 

//...

'params' => array(

        //...

        'preset' => array(

            'thumbs' => array(

                'name' => 'thumbs',

                'height' => 30,

                'width' => 50,

            ),

            'w300' => array(

                'name' => 'w300',

                'height' => 150,

                'width' => 300,

            ),

        ),

    ),

//...



А во вьюхах можно использовать так(в виджеты так отдаю первую картинку):


($f = $model->getImg("w300")) ? $f[0] : ""

Как такое решение можно обыграть, более правильно с точки зрения Yii?

Как выглядит сильно зависит от того, как реализована очередь. Для http://gearman.org/ и https://aws.amazon.com/sns/ будет чуть отличаться.

Нет никакой точки зрения Yii. Сначала делаете чтобы работало как надо, потом рефакторите до приятного глазу удобного вида.