cacheKey); } /** * Banner model * @param int|null $id * @param array $columns * @param array $with * @return Banner | \bff\db\illuminate\Model | array */ public function banner($id = null, $columns = ['*'], array $with = []) { $model = $this->model('Banner'); if (! empty($id)) { return $model->one($id, $columns, $with); } return $model; } /** * Получаем данные о баннерах по ID позиции (frontend) * @param array $filter фильтр * @return array */ public function bannersList(array $filter = []) { $banners = Cache::rememberForever($this->cacheKey, function () { $banners = $this->banner()->with('regions') ->where('enabled', 1) ->where('show_start', '<=', date('Y-m-d 00:00')) ->orderBy('pos') ->orderBy('num') ->tag('banners.data') ->get()->keyBy('id')->toArray(); foreach ($banners as &$v) { # данные о разделах $v['pages_all'] = 0; $v['pages_index'] = 0; $v['pages'] = $v['pages'] ?? []; if (!empty($v['pages'])) { $v['pages_all'] = in_array(static::PAGE_ANY, $v['pages']); $v['pages_index'] = in_array(static::PAGE_INDEX, $v['pages']); } if (method_exists($this, 'bannersListInjection')) { $this->bannersListInjection($v); } } unset($v); return $banners; }); if (! empty($filter['pos'])) { $positionID = $filter['pos']; $return = []; foreach ($banners as $k => $v) { if ($v['pos'] == $positionID) { $return[$k] = $v; } } return $return; } elseif (! empty($filter['id'])) { return (isset($banners[$filter['id']]) ? $banners[$filter['id']] : []); } else { return $banners; } } /** * Список баннеров (admin) * @param array $filter фильтр списка баннеров * @return mixed */ public function bannersListing(array $filter = []) { $query = $this->banner()->with('regions') ->select('B.*') ->selectRaw('SUM(S.shows) as shows') ->selectRaw('SUM(S.clicks) as clicks') ->from(static::TABLE_BANNERS, 'B') ->leftJoin(static::TABLE_BANNERS_STAT . ' as S', 'S.banner_id', '=', 'B.id') ->groupBy('B.id') ->tag('banners.listing'); if (isset($filter['region'])) { $region = Geo::regionData($filter['region']); if (! empty($region['numlevel'])) { $query->join(static::TABLE_BANNERS_REGIONS . ' as R', function ($join) use ($region) { $join->on('B.id', '=', 'R.banner_id'); $join->where('R.geo_region' . $region['numlevel'], '=', $region['id']); }); } unset($filter['region']); } if (isset($filter['query'])) { $search = '%' . $filter['query'] . '%'; $query->where(function ($q) use ($search) { $q->where('B.title', 'like', $search); $q->orWhere('B.alt', 'like', $search); $q->orWhere('B.description', 'like', $search); $q->orWhere('B.click_url', 'like', $search); }); unset($filter['query']); } if (isset($filter['locale'])) { $locale = $filter['locale']; $query->where(function ($q) use ($locale) { $q->where('B.locale', 'like', '%' . $locale . '%'); if ($locale == static::LOCALE_ALL) { $q->orWhere('B.locale', '=', ''); } }); unset($filter['locale']); } $fields = ['enabled', 'pos', 'show_start', 'show_finish']; foreach ($filter as $k => &$v) { if (is_numeric($k)) { if (is_array($v) && isset($v[0])) { if (in_array($v[0], $fields)) { $v[0] = 'B.' . $v[0]; } } } else { if (in_array($k, $fields) && is_scalar($v)) { $query->where('B.' . $k, $v); unset($filter[$k]); } } } unset($v); return $query->where($filter)->get()->toArray(); } /** * Сохранение/обновление баннера * @param int $bannerID ID баннера * @param array $data данные баннера * @return mixed */ public function bannerSave($bannerID, array $data = []) { if ($bannerID) { return $this->banner($bannerID)->update($data); } else { $banner = $this->banner()->fill($data); if ($banner->save()) { $bannerID = $banner->getKey(); } return $bannerID; } } /** * Получаем ID баннеров по ID позиции * @param int $positionID ID позиции * @return array ID баннеров связанных с указанной позицией */ public function bannersByPosition($positionID) { return $this->query(static::TABLE_BANNERS)->where('pos', $positionID)->pluck('id')->toArray(); } /** * Связываем баннеры с указанной позицией {$positionID} * @param array $bannersID ID баннеров * @param int $positionID ID позиции * @return int кол-во перемещенных баннеров */ public function bannersToPosition(array $bannersID, $positionID) { if (! empty($bannersID) && $positionID > 0) { return $this->query(static::TABLE_BANNERS)->whereIn('id', $bannersID)->update(['pos' => $positionID]); } return 0; } /** * Получаем ID следующего по счету баннера * @return int */ public function bannerNextID() { return (int)$this->banner()->max('id') + 1; } /** * Актуализация баннеров по дате/лимиту показов */ public function bannersCron() { # выключаем просроченные баннеры $resetCache = $this->query(static::TABLE_BANNERS)->tag('banners.off.show.finish') ->where(['enabled' => 1, ['show_finish', '<=', $this->now()]]) ->update(['enabled' => 0]); # выключаем баннеры с превышенным лимитом показов $bannersList = $this->query(static::TABLE_BANNERS, 'B')->tag('banners.off.show.limit') ->leftJoin(static::TABLE_BANNERS_STAT . ' as S', 'S.banner_id', '=', 'B.id') ->where('B.enabled', 1) ->where('B.show_limit', '>', 0) ->groupBy('B.id') ->havingRaw('B.show_limit <= SUM(S.shows)') ->get(['B.id', 'B.show_limit'])->keyBy('B.id')->toArray(); if (! empty($bannersList)) { $res = $this->query(static::TABLE_BANNERS) ->whereIn('id', array_keys($bannersList)) ->update(['enabled' => 0]); if (!empty($res)) { $resetCache = true; } } if (!empty($resetCache)) { $this->cacheReset(); } } /** * Подробная статистика просмотров/переходов (admin) * @param array $filter фильтр списка баннеров * @param bool $count только подсчет кол-ва * @param string $order * @param int $limit * @param int $offset * @return mixed */ public function bannerStatisticListing(array $filter = [], $count = false, $order = '', $limit = 0, $offset = 0) { if ($count) { return $this->query(static::TABLE_BANNERS_STAT)->where($filter)->count(); } return $this->query(static::TABLE_BANNERS_STAT)->select() ->selectRaw('ROUND(( clicks / ( (CASE WHEN shows > 0 THEN shows ELSE 1 END) ) ) * 100, 2) as ctr') ->where($filter) ->when($order, function ($q, $order) { $q->orderByRaw($order); }) ->limit($limit) ->offset($offset) ->get(); } /** * Перемещение баннера * @param int $positionID позиция * @return mixed @see rotateTablednd */ public function bannersRotate($positionID) { $this->cacheReset(); return $this->db->rotateTablednd(static::TABLE_BANNERS, ' AND pos = ' . $positionID); } # -------------------------------------------------------------------- # позиции /** * Banner position model * @param int|null $id * @param array $columns * @param array $with * @return \bff\modules\banners\models\Position | \bff\db\illuminate\Model | array */ public function position($id = null, $columns = ['*'], array $with = []) { $model = $this->model('Position'); if (! empty($id)) { return $model->one($id, $columns, $with); } return $model; } /** * Список позиций * @param array $filter фильтр списка позиций * @return mixed */ public function positionsList(array $filter = []) { $list = $this->position()->tag('banners.positions.list') ->withCount('banners as banners') ->where($filter) ->groupBy('id') ->orderBy('keyword') ->langJsonToString() ->get() ->keyBy('id') ; if (! empty($list)) { foreach ($list as $k => $v) { $list[$k]['sizes'] = ($v['width'] > 0 ? $v['width'] : '100%') . ' x ' . ($v['height'] > 0 ? $v['height'] : '100%'); } } return $list; } /** * Получение данных позиции * @param int $positionID ID позиции * @return array */ public function positionData($positionID) { return $this->position($positionID)->loadCount([ 'banners as banners', 'banners as banners_enabled' => function ($q) { $q->where('enabled', 1); }, ])->toArray(); } /** * Сохранение позиции * @param int $positionID ID позиции * @param array $positionData данные позиции * @return bool|int */ public function positionSave($positionID, array $positionData) { if (empty($positionData)) { return false; } if ($positionID > 0) { return $this->position($positionID)->update($positionData); } $position = $this->position()->fill($positionData); if ($position->save()) { $positionID = $position->getKey(); } return $positionID; } /** * Удаление позиции * @param int $positionID ID позиции * @return bool */ public function positionDelete($positionID) { if (empty($positionID)) { return false; } $res = $this->position($positionID)->delete(); if (!empty($res)) { return true; } return false; } /** * Проверка / формирование ключа позиции * @param string $positionKey ключ позиции * @param string $positionTitle название позиции * @param int|null $positionID ID позиции или null * @return string корректный ключ позиции */ public function positionKeywordValidate($positionKey, $positionTitle, $positionID = null) { return $this->db->getKeyword($positionKey, $positionTitle, static::TABLE_BANNERS_POS, $positionID); } }