Ответы пользователя по тегу Yii
  • Как в Yii2 реализовать мультивалютность?

    Для реализации задачи нужно следующее:
    1. В таблице товаров, помимо поля price, ещё нужно иметь поле currency_code, в котором хранить код валюты данного товара (ведь исходная цена товара может быть в USD, или EUR - не менять же её постоянно при колебаниях курса).

    2. Создать модуль для валют (таблица currency в БД, модель Currency и удобный КРУД для управления валютами). В таблице валют уникальный должен быть code - код валюты, по внешнему ключу связать его с полем currency_code в таблице товаров. Также у валюты должны быть поля rate (курс) и default (валюта по умолчанию для сайта). Для валюты по умолчанию rate должен быть равен 1.

    3. Нужно написать маленький компонент app/components/Currency.php, в котором через работу с сессией, сохранять в сессию выбранную пользователем валюту на сайте (выбор валюты через виджет удобно сделать). Также, во всех местах проекта, где выводится цена товара, она должна выводиться через компонент валют, чтобы он её преобразовывал из исходной (той, что в базе указана в качестве валюты для цены товара) в ту, что выбрал пользователь. Преобразование, соответственно, заданному в админке курсу (КРУД для валют, поле rate).

    Пример модели валют
    <?php
    
    namespace app\modules\shop\models;
    
    use Yii;
    use yii\db\ActiveRecord;
    use yii\helpers\ArrayHelper;
    
    class Currency extends ActiveRecord
    {
        const STATUS_ACTIVE = 1;
        const STATUS_INACTIVE = 0;
    
        public function behaviors()
        {
            return [
                'timestamp' => [
                    'class' => \yii\behaviors\TimestampBehavior::class,
                ],
                'blameable' => [
                    'class' => \yii\behaviors\BlameableBehavior::class,
                ],
                'ip' => [
                    'class' => \yii\behaviors\AttributeBehavior::class,
                    'attributes' => [
                        ActiveRecord::EVENT_BEFORE_INSERT => 'created_ip',
                        ActiveRecord::EVENT_BEFORE_UPDATE => 'updated_ip',
                    ],
                    'value' => function ($event) {
                        return Yii::$app->request->getUserIP();
                    },
                ],
            ];
        }
    
        public static function tableName()
        {
            return 'currency';
        }
    
        public function rules()
        {
            return [
                [['name', 'code'], 'required'],
                [['is_default', 'sort', 'status'], 'integer'],
                [['decimal_places'], 'integer', 'max' => 9],
                [['rate'], 'number'],
                [['code'], 'string', 'length' => 3],
                [['code'], 'unique'],
                [['code'], 'match', 'pattern' => '/^[A-Z]+$/', 'message' => Yii::t('app', 'Это поле может содержать только строчные буквы, цифры и дефис')],
                [['name', 'symbol'], 'string', 'max' => 32],
            ];
        }
    
        public function attributeLabels()
        {
            return [
                'id' => Yii::t('app', 'ID'),
                'created_at' => Yii::t('app', 'Дата создания'),
                'updated_at' => Yii::t('app', 'Дата обновления'),
                'created_by' => Yii::t('app', 'Создано пользователем'),
                'updated_by' => Yii::t('app', 'Обновлено пользователем'),
                'created_ip' => Yii::t('app', 'Создано c IP'),
                'updated_ip' => Yii::t('app', 'Обновлено c IP'),
                'code' => Yii::t('app', 'Код'),
                'name' => Yii::t('app', 'Название'),
                'symbol' => Yii::t('app', 'Символ'),
                'rate' => Yii::t('app', 'Курс'),
                'decimal_places' => Yii::t('app', 'Количество знаков после запятой'),
                'is_default' => Yii::t('app', 'По умолчанию'),
                'sort' => Yii::t('app', 'Сортировка'),
                'status' => Yii::t('app', 'Опубликован'),
            ];
        }
       
        public function afterSave($insert, $changedAttributes)
        {
            return Yii::$app->session->remove('currency');
        }
        
        public static function listItems()
        {
            $items = self::find()
                ->select(['code'])
                ->where(['status' => self::STATUS_ACTIVE])
                ->orderBy(['sort' => SORT_ASC])
                ->asArray()
                ->all();
            return ArrayHelper::map($items, 'code', 'code');
        }
        
        public static function statuses()
        {
            return [
                self::STATUS_ACTIVE => Yii::t('app', 'Опубликован'),
                self::STATUS_INACTIVE => Yii::t('app', 'Не опубликован'),
            ];
        }
    }

    Пример компонента валют
    <?php
    
    namespace app\modules\shop\components;
    
    use Yii;
    use yii\base\BaseObject;
    use app\modules\shop\models\Currency;
    
    class CurrencyManager extends BaseObject
    {
        public $currencies;
        public $defaultCurrency;
        public $siteCurrency;
        
        public function init()
        {
            parent::init();
            
            $this->currencies = Currency::find()
                ->where(['status' => Currency::STATUS_ACTIVE])
                ->orderBy(['sort' => SORT_ASC])
                ->asArray()
                ->all();
            foreach ($this->currencies as $item) {
                if ($item['is_default'] == 1) {
                    $this->defaultCurrency = $item;
                }
            }
            if (!Yii::$app->session['currency']) {
                Yii::$app->session['currency'] = $this->defaultCurrency;
            }
            $this->siteCurrency = Yii::$app->session['currency'];
        }
        
        public function listCurrencies()
        {
            $result = [];
            foreach ($this->currencies as $item) {
                $result[$item['code']] = $item['code'];
            }
            return $result;
        }
        
        public function showPrice($offer, $symbol = true)
        {
            if ($offer['currency_code'] == $this->siteCurrency['code']) {
                $price = $offer['price'];
            } else {
                foreach ($this->currencies as $item) {
                    if ($item['code'] == $offer['currency_code']) {
                        $rate = $item['rate'];
                    }
                }
                $price = ($offer['price'] * $rate) / $this->siteCurrency['rate'];
            }
                    
            if ($symbol === true) {
                return number_format($price, $this->siteCurrency['decimal_places'], '.', '') . '&nbsp;' . $this->siteCurrency['symbol'];
            } else {
                //return round($price, $this->siteCurrency['decimal_places']);
                return number_format($price, $this->siteCurrency['decimal_places'], '.', '');
            }
        }
        
        public function showOldPrice($offer, $symbol = true)
        {
            if ($offer['currency_code'] == $this->siteCurrency['code']) {
                $price = $offer['old_price'];
            } else {
                foreach ($this->currencies as $item) {
                    if ($item['code'] == $offer['currency_code']) {
                        $rate = $item['rate'];
                    }
                }
                $price = ($offer['old_price'] * $rate) / $this->siteCurrency['rate'];
            }
                    
            if ($symbol === true) {
                return number_format($price, $this->siteCurrency['decimal_places'], '.', '') . '&nbsp;' . $this->siteCurrency['symbol'];
            } else {
                return round($price, $this->siteCurrency['decimal_places']);
            }
        }
    }

    Ответ написан
    Комментировать
  • CMS на базе Yii2?

    Я пересмотрел несколько существующих разработок CMS (если их вообще можно так назвать на данном этапе) на базе Yii2 - Skeeks, Easyii, Dotplant итд. Это всё просто большие куски кода, эксперименты... Никакой чёткой концепции, структуры, ничего нет. Yii2 - это фреймворк не для создания CMS однозначно. У меня есть несколько готовых проектов (интернет-магазины) на базе этого фреймворка - все очень быстро работают, всё ОК. Я создавал свои модели товаров, категорий, статей, прайсов итд. Есть SEO, удобная админка с контролем заказов и быстрых заявок, мультиязычность, корзина, модуль мультивалютности, экспорт прайсов в XML, разделение цен по уровню доступа для оптовиков и прочие базовые фишки, которые используются интернет-магазинах. Даже обновление цен и наличия через Excel. Всё сделано под заказчика. Но... Они все заточены под конкретную задачу, "реюзать" их код не получится (разве что, частично с передалками). Зато очень удобно расширять функционал и поддерживать такие проекты - нет никаких ограничений. Да и с безопасностью всё нормально, никаких вам бекдоров или "детских" болезней, характерных для популярных CMS.
    Если ищете готовые CMS решения на базе Yii2 Framework - их нет. Лучше создать своё. А ещё лучше - скомбинировать. Например, как базовая CMS только для контента - Wordpress, Modx или Joomla (или любая другая популярная CMS с хорошим большим сообществом и большим количеством дополнений), а для дополнительного функционала - устанавливайте фреймворк Yii2 на поддомен и подключайте его через API или напрямую к базе основной CMS (через модели).
    Ответ написан
    1 комментарий
  • Мультиязычность в Yii2: как реализовать правила URL?

    Я использую сдедующий подход:
    1) В базе данных информация о страницах (или товарах, или других сущностях) хранится в двух таблицах: например, page и page_content. Первая таблица - это данные, которые не требуют перевода (например id, дата создания, дата обновления, слаг итд.). Вторая таблица - это текстовые данные, которые будут мультиязычными (meta_title, meta_description, meta_keywords, content). Во второй таблице (с контентом), должно быть поле для связи с первой таблицой (напр. поле page_id), а также поле для указания кода языка. Получается, что для одной записи в первой таблице существует несколько записей во второй таблице, каждая из которых - на разных языках.
    2) В моделях нужно прописать связь один к одному для этих таблиц, в которых связующими будут поля по ID и по полю языка (параметр берётся из настроек приложения Yii::$app->language):
    public function getPageContent()
    {
        return $this->hasOne(PageContent::className(), ['page_id' => 'id'])->andWhere(['page_content.language_code' => Yii::$app->language]);
    }

    Теперь при выборке данных из первой таблицы, будут подтягиваться тексты из второй таблицы, на текущем языке приложения.
    3) Для управления текущим языком приложения и удобного переключения с одного языка на другой (параметр языка хранится в ссылке), существует замечательный компонент Yii2 Locale URLs
    Ответ написан
    1 комментарий