ver 0.262 - pages module refactor and admin UX fixes
This commit is contained in:
626
autoload/Domain/Pages/PagesRepository.php
Normal file
626
autoload/Domain/Pages/PagesRepository.php
Normal file
@@ -0,0 +1,626 @@
|
||||
<?php
|
||||
namespace Domain\Pages;
|
||||
|
||||
class PagesRepository
|
||||
{
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const PAGE_TYPES = [
|
||||
0 => 'pelne artykuly',
|
||||
1 => 'wprowadzenia',
|
||||
2 => 'miniaturki',
|
||||
3 => 'link',
|
||||
4 => 'kontakt',
|
||||
5 => 'kategoria sklepu',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private const SORT_TYPES = [
|
||||
0 => 'data dodania - najstarsze na poczatku',
|
||||
1 => 'data dodania - najnowsze na poczatku',
|
||||
2 => 'data modyfikacji - rosnaco',
|
||||
3 => 'data modyfikacji - malejaco',
|
||||
4 => 'reczne',
|
||||
5 => 'alfabetycznie - A - Z',
|
||||
6 => 'alfabetycznie - Z - A',
|
||||
];
|
||||
|
||||
private $db;
|
||||
|
||||
public function __construct($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function pageTypes(): array
|
||||
{
|
||||
return self::PAGE_TYPES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function sortTypes(): array
|
||||
{
|
||||
return self::SORT_TYPES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function menusList(): array
|
||||
{
|
||||
$rows = $this->db->select('pp_menus', '*', ['ORDER' => ['id' => 'ASC']]);
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function menusWithPages(): array
|
||||
{
|
||||
$menus = $this->menusList();
|
||||
foreach ($menus as $index => $menu) {
|
||||
$menuId = (int)($menu['id'] ?? 0);
|
||||
$menus[$index]['pages'] = $this->menuPages($menuId);
|
||||
}
|
||||
|
||||
return $menus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function menuPages(int $menuId, ?int $parentId = null): array
|
||||
{
|
||||
if ($menuId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_pages', ['id', 'menu_id', 'status', 'parent_id', 'start'], [
|
||||
'AND' => [
|
||||
'menu_id' => $menuId,
|
||||
'parent_id' => $parentId,
|
||||
],
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
|
||||
if (!is_array($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$pages = [];
|
||||
foreach ($rows as $row) {
|
||||
$pageId = (int)($row['id'] ?? 0);
|
||||
if ($pageId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row['title'] = $this->pageTitle($pageId);
|
||||
$row['languages'] = $this->pageLanguages($pageId);
|
||||
$row['subpages'] = $this->menuPages($menuId, $pageId);
|
||||
$pages[] = $row;
|
||||
}
|
||||
|
||||
return $pages;
|
||||
}
|
||||
|
||||
public function menuDelete(int $menuId): bool
|
||||
{
|
||||
if ($menuId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((int)$this->db->count('pp_pages', ['menu_id' => $menuId]) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)$this->db->delete('pp_menus', ['id' => $menuId]);
|
||||
}
|
||||
|
||||
public function pageDelete(int $pageId): bool
|
||||
{
|
||||
if ($pageId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((int)$this->db->count('pp_pages', ['parent_id' => $pageId]) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)$this->db->delete('pp_pages', ['id' => $pageId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function menuDetails(int $menuId): array
|
||||
{
|
||||
if ($menuId <= 0) {
|
||||
return [
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
'status' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
$menu = $this->db->get('pp_menus', '*', ['id' => $menuId]);
|
||||
if (!is_array($menu)) {
|
||||
return [
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
'status' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
public function menuSave(int $menuId, string $name, $status): bool
|
||||
{
|
||||
$statusValue = $this->toSwitchValue($status);
|
||||
|
||||
if ($menuId <= 0) {
|
||||
$result = $this->db->insert('pp_menus', [
|
||||
'name' => $name,
|
||||
'status' => $statusValue,
|
||||
]);
|
||||
|
||||
return (bool)$result;
|
||||
}
|
||||
|
||||
$this->db->update('pp_menus', [
|
||||
'name' => $name,
|
||||
'status' => $statusValue,
|
||||
], [
|
||||
'id' => $menuId,
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function pageDetails(int $pageId): array
|
||||
{
|
||||
if ($pageId <= 0) {
|
||||
return $this->defaultPage();
|
||||
}
|
||||
|
||||
$page = $this->db->get('pp_pages', '*', ['id' => $pageId]);
|
||||
if (!is_array($page)) {
|
||||
return $this->defaultPage();
|
||||
}
|
||||
|
||||
$translations = $this->db->select('pp_pages_langs', '*', ['page_id' => $pageId]);
|
||||
if (is_array($translations)) {
|
||||
foreach ($translations as $row) {
|
||||
$langId = (string)($row['lang_id'] ?? '');
|
||||
if ($langId !== '') {
|
||||
$page['languages'][$langId] = $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$page['layout_id'] = (int)$this->db->get('pp_layouts_pages', 'layout_id', ['page_id' => $pageId]);
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function pageArticles(int $pageId): array
|
||||
{
|
||||
if ($pageId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sql = '
|
||||
SELECT
|
||||
ap.article_id,
|
||||
ap.o,
|
||||
a.status,
|
||||
(
|
||||
SELECT title
|
||||
FROM pp_articles_langs AS pal
|
||||
JOIN pp_langs AS pl ON pal.lang_id = pl.id
|
||||
WHERE pal.article_id = ap.article_id
|
||||
AND pal.title != ""
|
||||
ORDER BY pl.o ASC
|
||||
LIMIT 1
|
||||
) AS title
|
||||
FROM pp_articles_pages AS ap
|
||||
JOIN pp_articles AS a ON a.id = ap.article_id
|
||||
WHERE ap.page_id = :page_id
|
||||
AND a.status != -1
|
||||
ORDER BY ap.o ASC
|
||||
';
|
||||
|
||||
$stmt = $this->db->query($sql, [':page_id' => $pageId]);
|
||||
$rows = $stmt ? $stmt->fetchAll() : [];
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
public function saveArticlesOrder(int $pageId, $articles): bool
|
||||
{
|
||||
if ($pageId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_array($articles)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->db->update('pp_articles_pages', ['o' => 0], ['page_id' => $pageId]);
|
||||
|
||||
$position = 0;
|
||||
foreach ($articles as $item) {
|
||||
$articleId = (int)($item['item_id'] ?? 0);
|
||||
if ($articleId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$position++;
|
||||
$this->db->update('pp_articles_pages', ['o' => $position], [
|
||||
'AND' => [
|
||||
'page_id' => $pageId,
|
||||
'article_id' => $articleId,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function savePagesOrder(int $menuId, $pages): bool
|
||||
{
|
||||
if ($menuId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_array($pages)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->db->update('pp_pages', ['o' => 0], ['menu_id' => $menuId]);
|
||||
|
||||
$position = 0;
|
||||
foreach ($pages as $item) {
|
||||
$itemId = (int)($item['item_id'] ?? 0);
|
||||
$depth = (int)($item['depth'] ?? 0);
|
||||
if ($itemId <= 0 || $depth <= 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentId = (int)($item['parent_id'] ?? 0);
|
||||
if ($depth === 2) {
|
||||
$parentId = null;
|
||||
}
|
||||
|
||||
$position++;
|
||||
$this->db->update('pp_pages', [
|
||||
'o' => $position,
|
||||
'parent_id' => $parentId,
|
||||
], [
|
||||
'id' => $itemId,
|
||||
]);
|
||||
}
|
||||
|
||||
\S::delete_dir('../temp/');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function pageSave(array $data): ?int
|
||||
{
|
||||
$pageId = (int)($data['id'] ?? 0);
|
||||
$menuId = (int)($data['menu_id'] ?? 0);
|
||||
$parentId = $this->normalizeNullableInt($data['parent_id'] ?? null);
|
||||
$pageType = (int)($data['page_type'] ?? 0);
|
||||
$sortType = (int)($data['sort_type'] ?? 0);
|
||||
$layoutId = (int)($data['layout_id'] ?? 0);
|
||||
$articlesLimit = (int)($data['articles_limit'] ?? 0);
|
||||
$showTitle = $this->toSwitchValue($data['show_title'] ?? 0);
|
||||
$status = $this->toSwitchValue($data['status'] ?? 0);
|
||||
$start = $this->toSwitchValue($data['start'] ?? 0);
|
||||
$categoryId = $this->normalizeNullableInt($data['category_id'] ?? null);
|
||||
if ($pageType !== 5) {
|
||||
$categoryId = null;
|
||||
}
|
||||
|
||||
if ($pageId <= 0) {
|
||||
$order = $this->maxPageOrder() + 1;
|
||||
$result = $this->db->insert('pp_pages', [
|
||||
'menu_id' => $menuId,
|
||||
'page_type' => $pageType,
|
||||
'sort_type' => $sortType,
|
||||
'articles_limit' => $articlesLimit,
|
||||
'show_title' => $showTitle,
|
||||
'status' => $status,
|
||||
'o' => $order,
|
||||
'parent_id' => $parentId,
|
||||
'start' => $start,
|
||||
'category_id' => $categoryId,
|
||||
]);
|
||||
|
||||
if (!$result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$pageId = (int)$this->db->id();
|
||||
if ($pageId <= 0) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$this->db->update('pp_pages', [
|
||||
'menu_id' => $menuId,
|
||||
'page_type' => $pageType,
|
||||
'sort_type' => $sortType,
|
||||
'articles_limit' => $articlesLimit,
|
||||
'show_title' => $showTitle,
|
||||
'status' => $status,
|
||||
'parent_id' => $parentId,
|
||||
'start' => $start,
|
||||
'category_id' => $categoryId,
|
||||
], [
|
||||
'id' => $pageId,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($start === 1) {
|
||||
$this->db->update('pp_pages', ['start' => 0], ['id[!]' => $pageId]);
|
||||
$this->db->update('pp_pages', ['start' => 1], ['id' => $pageId]);
|
||||
}
|
||||
|
||||
$this->db->delete('pp_layouts_pages', ['page_id' => $pageId]);
|
||||
if ($layoutId > 0) {
|
||||
$this->db->insert('pp_layouts_pages', [
|
||||
'layout_id' => $layoutId,
|
||||
'page_id' => $pageId,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->saveTranslations($pageId, $pageType, $data);
|
||||
$this->updateSubpagesMenuId($pageId, $menuId);
|
||||
|
||||
\S::htacces();
|
||||
\S::delete_dir('../temp/');
|
||||
|
||||
return $pageId;
|
||||
}
|
||||
|
||||
public function generateSeoLink(string $title, int $pageId = 0, int $articleId = 0, int $categoryId = 0): string
|
||||
{
|
||||
$base = trim((string)\S::seo($title));
|
||||
if ($base === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$candidate = $base;
|
||||
$suffix = 0;
|
||||
|
||||
while ($this->isSeoLinkUsed('pp_pages_langs', 'page_id', $candidate, $pageId)
|
||||
|| $this->isSeoLinkUsed('pp_articles_langs', 'article_id', $candidate, $articleId)
|
||||
|| $this->isSeoLinkUsed('pp_shop_categories_langs', 'category_id', $candidate, $categoryId)) {
|
||||
$suffix++;
|
||||
$candidate = $base . '-' . $suffix;
|
||||
}
|
||||
|
||||
return $candidate;
|
||||
}
|
||||
|
||||
public function pageUrlPreview(int $pageId, string $langId, string $title, string $seoLink, string $defaultLanguageId): string
|
||||
{
|
||||
$url = trim($seoLink) !== ''
|
||||
? '/' . ltrim($seoLink, '/')
|
||||
: '/s-' . $pageId . '-' . \S::seo($title);
|
||||
|
||||
if ($langId !== '' && $langId !== $defaultLanguageId && $url !== '#') {
|
||||
$url = '/' . $langId . $url;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
public function toggleCookieValue(string $cookieName, int $itemId): void
|
||||
{
|
||||
if ($cookieName === '' || $itemId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$state = [];
|
||||
if (!empty($_COOKIE[$cookieName])) {
|
||||
$decoded = @unserialize((string)$_COOKIE[$cookieName], ['allowed_classes' => false]);
|
||||
if (is_array($decoded)) {
|
||||
$state = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
$state[$itemId] = empty($state[$itemId]) ? 1 : 0;
|
||||
setcookie($cookieName, serialize($state), time() + 3600 * 24 * 365);
|
||||
}
|
||||
|
||||
public function pageTitle(int $pageId): string
|
||||
{
|
||||
if ($pageId <= 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_pages_langs', [
|
||||
'[><]pp_langs' => ['lang_id' => 'id'],
|
||||
], 'title', [
|
||||
'AND' => [
|
||||
'page_id' => $pageId,
|
||||
'title[!]' => '',
|
||||
],
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
'LIMIT' => 1,
|
||||
]);
|
||||
|
||||
if (!is_array($rows) || !isset($rows[0])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (string)$rows[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function pageLanguages(int $pageId): array
|
||||
{
|
||||
if ($pageId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->select('pp_pages_langs', '*', [
|
||||
'AND' => [
|
||||
'page_id' => $pageId,
|
||||
'title[!]' => null,
|
||||
],
|
||||
]);
|
||||
|
||||
return is_array($rows) ? $rows : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function defaultPage(): array
|
||||
{
|
||||
return [
|
||||
'id' => 0,
|
||||
'menu_id' => 0,
|
||||
'page_type' => 0,
|
||||
'sort_type' => 0,
|
||||
'articles_limit' => 2,
|
||||
'show_title' => 1,
|
||||
'status' => 1,
|
||||
'start' => 0,
|
||||
'parent_id' => null,
|
||||
'category_id' => null,
|
||||
'layout_id' => 0,
|
||||
'languages' => [],
|
||||
];
|
||||
}
|
||||
|
||||
private function saveTranslations(int $pageId, int $pageType, array $data): void
|
||||
{
|
||||
$titles = is_array($data['title'] ?? null) ? $data['title'] : [];
|
||||
$seoLinks = is_array($data['seo_link'] ?? null) ? $data['seo_link'] : [];
|
||||
$metaTitles = is_array($data['meta_title'] ?? null) ? $data['meta_title'] : [];
|
||||
$metaDescriptions = is_array($data['meta_description'] ?? null) ? $data['meta_description'] : [];
|
||||
$metaKeywords = is_array($data['meta_keywords'] ?? null) ? $data['meta_keywords'] : [];
|
||||
$noindexValues = is_array($data['noindex'] ?? null) ? $data['noindex'] : [];
|
||||
$pageTitles = is_array($data['page_title'] ?? null) ? $data['page_title'] : [];
|
||||
$links = is_array($data['link'] ?? null) ? $data['link'] : [];
|
||||
$canonicals = is_array($data['canonical'] ?? null) ? $data['canonical'] : [];
|
||||
|
||||
foreach ($titles as $langId => $title) {
|
||||
$langId = (string)$langId;
|
||||
if ($langId === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row = [
|
||||
'lang_id' => $langId,
|
||||
'title' => $this->nullIfEmpty($title),
|
||||
'meta_description' => $this->nullIfEmpty($metaDescriptions[$langId] ?? null),
|
||||
'meta_keywords' => $this->nullIfEmpty($metaKeywords[$langId] ?? null),
|
||||
'meta_title' => $this->nullIfEmpty($metaTitles[$langId] ?? null),
|
||||
'seo_link' => $this->nullIfEmpty(\S::seo((string)($seoLinks[$langId] ?? ''))),
|
||||
'noindex' => (int)($noindexValues[$langId] ?? 0),
|
||||
'page_title' => $this->nullIfEmpty($pageTitles[$langId] ?? null),
|
||||
'link' => $pageType === 3 ? $this->nullIfEmpty($links[$langId] ?? null) : null,
|
||||
'canonical' => $this->nullIfEmpty($canonicals[$langId] ?? null),
|
||||
];
|
||||
|
||||
$translationId = (int)$this->db->get('pp_pages_langs', 'id', [
|
||||
'AND' => [
|
||||
'page_id' => $pageId,
|
||||
'lang_id' => $langId,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($translationId > 0) {
|
||||
$this->db->update('pp_pages_langs', $row, ['id' => $translationId]);
|
||||
} else {
|
||||
$row['page_id'] = $pageId;
|
||||
$this->db->insert('pp_pages_langs', $row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function updateSubpagesMenuId(int $parentId, int $menuId): void
|
||||
{
|
||||
if ($parentId <= 0 || $menuId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->update('pp_pages', ['menu_id' => $menuId], ['parent_id' => $parentId]);
|
||||
|
||||
$children = $this->db->select('pp_pages', ['id'], ['parent_id' => $parentId]);
|
||||
if (!is_array($children)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($children as $row) {
|
||||
$childId = (int)($row['id'] ?? 0);
|
||||
if ($childId > 0) {
|
||||
$this->updateSubpagesMenuId($childId, $menuId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isSeoLinkUsed(string $table, string $idColumn, string $seoLink, int $exceptId): bool
|
||||
{
|
||||
$where = [
|
||||
'seo_link' => $seoLink,
|
||||
];
|
||||
|
||||
if ($exceptId > 0) {
|
||||
$where[$idColumn . '[!]'] = $exceptId;
|
||||
}
|
||||
|
||||
return (int)$this->db->count($table, ['AND' => $where]) > 0;
|
||||
}
|
||||
|
||||
private function maxPageOrder(): int
|
||||
{
|
||||
$max = $this->db->max('pp_pages', 'o');
|
||||
return $max ? (int)$max : 0;
|
||||
}
|
||||
|
||||
private function toSwitchValue($value): int
|
||||
{
|
||||
if ($value === 'on' || $value === '1' || $value === 1 || $value === true) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function normalizeNullableInt($value): ?int
|
||||
{
|
||||
if ($value === null || $value === '' || (int)$value === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
private function nullIfEmpty($value): ?string
|
||||
{
|
||||
$value = trim((string)$value);
|
||||
return $value === '' ? null : $value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user