Форум программистов, компьютерный форум, киберфорум
PHP для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.83/18: Рейтинг темы: голосов - 18, средняя оценка - 4.83
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,076

Органицация пагинации

04.07.2020, 23:48. Показов 3725. Ответов 31
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Наваял свой первый пагинатор. Сначала естественно в процедурном стиле, потом еще половина этого времени ушло, чтобы пееформатировать в ООП. С этим конечно возникли сложности, т.к. для себя я вижу разные варианты, непонятно как правильно/грамотно. Ну и сами вопросы по реализации, большей частью по представлению.
Выглядит и функционирует это конечно примитивно, но функции свои выполняет:
Название: Screenshot from 2020-07-04 22-11-51.jpg
Просмотров: 214

Размер: 3.7 Кб Название: Screenshot from 2020-07-04 22-11-42.jpg
Просмотров: 214

Размер: 3.1 Кб Название: Screenshot from 2020-07-04 22-11-33.jpg
Просмотров: 214

Размер: 2.8 Кб

Реализовал так: Во фронт-контроллере вызываю основной контроллер(для вывода статей) и получаю объект пагинатора, который с объектом статьи и комментариев рендерятся в страничку представления:
Кликните здесь для просмотра всего текста

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
 * Class MainController
 * @package MyProject\Controllers
 */
class MainController extends AbstractController
{
    /**
     * @param int $active
     * @throws DbException
     * @throws NotFoundException
     */
    public function main(int $active = 1)
    {
        $articles = Article::getAll();
        $comments = Comment::getAll();
        if (is_array($articles)) {
            $articlesInDb = count($articles);
            $articlesPerPage = 3;
            $pages = ceil($articlesInDb / $articlesPerPage);
            if ($active <=0 || $active > $pages) {
                throw new NotFoundException('Wrong page number');
            }
            $firstArticleOnPage = ($active*$articlesPerPage)-$articlesPerPage;
            $articles = Article::getByLimit($firstArticleOnPage, $articlesPerPage);
            $paginateString = new Paginator($active, $pages);
        }
        $this->view->renderHtml('main/main.php', ['articles' => $articles, 'comments' => $comments, 'paginateString' => $paginateString]);
        exit();
    }
}

Пагинатор реализовал отдельным классом с конструктором и методом получения строки пагинатора с адресами.
Кликните здесь для просмотра всего текста

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/**
 * Class paginator
 * @package MyProject\Services
 */
class Paginator
{
    /**
     * @var
     */
    protected $sideSize;
    /**
     * @var int
     */
    protected $active;
    /**
     * @var int
     */
    protected $pages;
    /**
     * @var
     */
    protected $start;
    /**
     * @var
     */
    protected $stop;
    /**
     * @return mixed
     */
    public function getSideSize()
    {
        return $this->sideSize;
    }
 
    /**
     * @return mixed
     */
    public function getStart()
    {
        return $this->start;
    }
 
    /**
     * @return mixed
     */
    public function getStop()
    {
        return $this->stop;
    }
 
    /**
     * @return int
     */
    public function getActive(): int
    {
        return $this->active;
    }
 
    /**
     * @return int
     */
    public function getPages(): int
    {
        return $this->pages;
    }
 
    /**
     * paginator constructor.
     * @param int $active
     * @param int $pages
     */
    public function __construct(int $active, int $pages)
    {
        $this->active = $active;
        $this->pages = $pages;
        $this->getPaginateString();
    }
 
    /**
     * @return $this
     */
    private function getPaginateString()
    {
        $this->sideSize = 2;
        $paginatePagesNumber = $this->sideSize * 2 + 1;
        $this->start = $this->active - $this->sideSize;
        $this->stop = $this->active + $this->sideSize;
       if ($this->active < ($this->sideSize + 1)){
           $this->start = 1;
           $this->stop = $paginatePagesNumber;
        }
        if ($this->active + $this->sideSize > $this->pages) {
            $this->start = $this->pages - ($paginatePagesNumber - 1);
            $this->stop = $this->pages;
        }
        return $this;
    }
}

Тут конечно у меня своя логика, хотя нюансов хоть отбавляй)) Возможно многим покажется странным, непонятным, нелогичным, но оно работает.

Представление, наверное самое несуразное, тут сама строка пагинатора:
Кликните здесь для просмотра всего текста

HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<p>
    <a href="/blog/1"><<&nbsp</a>
    <a href="/blog/<?=$paginateString->getActive() == 1 ? 1 : $paginateString->getActive() - 1?>"><&nbsp</a>
 
    <?php
 
    if ($paginateString->getActive() > ($paginateString->getSideSize() + 1)) {
        echo '...';
    }?>
    <?php
 
    for ($start = $paginateString->getStart(); $start <= $paginateString->getStop(); $start++):?>
        <a href="/blog/<?=$start?>"><?=$start == $paginateString->getActive() ? '<span>'.$start.'</span>' : $start;?></a>
    <?php endfor?>
    <?php if ($paginateString->getActive() < ($paginateString->getPages() - $paginateString->getSideSize())) {
        echo '...';
    }?>
    <a href="/blog/<?=$paginateString->getActive() == $paginateString->getPages() ? $paginateString->getPages() : $paginateString->getActive() + 1?>">&nbsp></a>
    <a href="/blog/<?=$paginateString->getPages()?>">&nbsp>></a>
</p>

и кучка вопросов:
В части контроллера:
1. насколько корректно несколькими маршрутами отправлять на одну и ту же страничку? Страничка одна, а адресов несколько. Эту "проблему " не знал как по другому решить. Т.к. главная страничка - это первая страничка со статьями. А при постраничном вводе нужно как -то в составе стандартных ссылок пагинатора отправлять на первую страничку(/blog/1) = главную(/). Поэтому в контроллере задал "дефолтный" параметр. чтобы и через пагинатор можно было перейти на первую страничку и через ссылку "HOME". Количество статей на экран - опция выбора пользователем, пока захардкодил.

В части самого пагинатора:
2. Нужно ли тут вообще делать конструктор(субъективно, не вижу пока в нем необходимости, может для чего то пригодится в будущем) или оставить один метод, в который передавать параметры вместо конструктора?
3. Количество видимых страниц в строке пагинаора тоже захардкодил. Не знаю, как с ним поступать. Необходимость в такой опции - наверное скорее - экзотика и только для админа. В коде - для прямого программирования этой опции - достаточно только один раз изменить значение в методе.

В части представления:
4.Тут какая-то куча-мала... груда однотипных обращений к объектам, сам читаю и дурно становится. Может хоть где-то в начале представления пагинатора выполнить типа инициализацию объекта пагинатора и установить какие-то короткие, читаемые названия, получив в них данные через геттеры, как сделано для переменной $start в цикле?
5. на счет
PHP
1
echo'...'
; тут тоже большие сомнения в корректности такого решения. Хотя это больше к фонту относится я так понимаю,
но наверное все таки было бы как минимум лучше сделать через CSS (before/after)?

Ну и в целом - насколько все плохо?)
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
04.07.2020, 23:48
Ответы с готовыми решениями:

Оформление пагинации
Здраствуйте, не могу нормально оформить пагинацию пока сделал , что бы показывало как &quot;&quot;, через ид в стилях не работает. ...

Не отображается вторая страницы пагинации
На сайте есть раздел Акции, первая страница отображается нормально, когда перехожу на вторую страницу пагинации появляется вот это(скрин в...

Сохранение данных фильтра при пагинации
Добрый день. Подскажите пожалуйста, проблема в общем стандартная, но ответа не нашел. Есть каталог, подгружаемый из БД, есть фильтры,...

31
 Аватар для sad67man
2603 / 1507 / 689
Регистрация: 23.08.2015
Сообщений: 3,823
05.07.2020, 00:16
Лучший ответ Сообщение было отмечено СергейСереб как решение

Решение

СергейСереб, Объект пагинации нужен, чтоб хранить основную логику внутри, а вы делаете часть рассчетов снаружи - это ошибка.
Для организации пагинации нам необходимо знать общее кол-во элементов, поэтому потребуется сделать запрос дважды, один раз выбрать COUNT(id), второй раз уже используя LIMIT И OFFSET. Для расчета LIMIT и OFFSET нам как раз и потребуется объект пагинации. который внутри будет хранить логику расчетов.



PHP
1
2
3
4
5
6
7
8
9
10
11
12
class CommentReader
{
    public function count($articleId): int
    {
        //...
    }
 
    public function list($articleId, int $limit = 10, int $offset = 0): array
    {
        //...
    }
}
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Pagination
{
    private $totalCount;
    private $currentPage;
    private $limit = 5;
 
    public function __construct(int $totalCount, int $currentPage = 1)
    {
        $this->totalCount = $totalCount;
        $this->currentPage = $currentPage;
 
    }
 
    public function getLimit(): int
    {
        return $this->limit;
    }
 
    public function getOffset()
    {
        return ($this->getCurrentPage() - 1) * $this->getLimit();
    }
 
    public function getPagesCount()
    {
        return ceil($this->totalCount / $this->getLimit());
    }
 
    public function getCurrentPage()
    {
        return max(1, min($this->getPagesCount(), $this->currentPage));
    }
 
    /**
     * @return PaginationItem[]
     */
    public function getPages(): array
    {
        $start = max(1, $this->getCurrentPage() - 2);
        $end = min($this->getPagesCount(), $start + 5);
        $start = max(1, $end - 5);
 
        $pages = [];
 
        for($i = $start; $i <= $end; $i++) {
            $pages[] = new PaginationItem(
                $i,
                $i === $this->getCurrentPage()
            );
        }
 
        return $pages;
    }
}
PHP
1
2
3
4
5
6
7
8
9
10
11
class PaginationItem
{
    public $number;
    public $isCurrent = false;
 
    public function __construct(int $number, bool $isCurrent = false)
    {
        $this->number = $number;
        $this->isCurrent = $isCurrent;
    }
}
Ну и собственно использование

PHP
1
2
3
$article = $this->articles->getById(3);
$pagination = new Pagination($this->comments->count($article->id), $_GET['page'] ?? 1);
$comments = $this->comments->list($article->id, $pagination->getLimit(), $pagination->getOffset());
И вывод в шаблоне
PHP/HTML
1
2
3
4
5
6
7
<?php foreach ($pagination->getPages() as $page): ?>
    <?php if ($page->isCurrent): ?>
        <b>[<?= $page->number ?>]</b>
    <?php else: ?>
        <a href="<?= $url . '&page=' . $page->number ?>">[<?= $page->number ?>]</a>
    <?php endif ?>
<?php endforeach ?>
Добавлено через 3 минуты
Код не проверял, накидал "на коленке". Главное чтоб была понятна основная идея.
0
1306 / 998 / 232
Регистрация: 01.10.2018
Сообщений: 3,882
05.07.2020, 09:28
Цитата Сообщение от СергейСереб Посмотреть сообщение
1. насколько корректно несколькими маршрутами отправлять на одну и ту же страничку?
Вполне нормально. Но я бы использовал для передачи номера GET-параметр. Так больше шансов, что вам удастся использовать один маршрут. Хотя вариативность адресного шаблона все равно присутствует (если вы в нем учитываете не только путь), когда для первой страницы вы используете неявную передачу номера.

Цитата Сообщение от СергейСереб Посмотреть сообщение
3. Количество видимых страниц в строке пагинаора тоже захардкодил. Не знаю, как с ним поступать. Необходимость в такой опции - наверное скорее - экзотика и только для админа. В коде - для прямого программирования этой опции - достаточно только один раз изменить значение в методе.
Не нужно считать админа мастером на все руки. Это опция для верстальщика. Соответственно можно в шаблоне "захардкодить".

Цитата Сообщение от СергейСереб Посмотреть сообщение
4.Тут какая-то куча-мала... груда однотипных обращений к объектам, сам читаю и дурно становится. Может хоть где-то в начале представления пагинатора выполнить типа инициализацию объекта пагинатора и установить какие-то короткие, читаемые названия, получив в них данные через геттеры, как сделано для переменной $start в цикле?
Конечно, нужно использовать переменные ("распакованные", в массиве или в виде свойств объекта-представления). В ваших специфичных объектах верстальщик не обязан разбираться.

Как выше в общем-то уже написали, для пагинации нужны две основные функции:
1) count($col) или сразу pagecount($col,$pp=10);
2) функция получения самого списка, принимающая смещение и лимит, либо опять-таки сразу $pn (номер страницы) и $pp=10 (количество элементов на странице).

Для "широкой" строки навигации, позволяющей переключаться не только на соседние страницы (и "крайние" страницы), также нужны функции или объект для задания "ширины" строки и для вычисления значений start и end (stop как-то непривычно звучит; наверное, это семантически не самое точное слово; также можно использовать слова first и last, если у вас при использовании этих слов не возникает путаницы с первой и последней существующими страницами). Можно вычислять и сразу весь набор номеров от start до end, как показали выше, но это немного сложнее.

Также, чтобы не было безобразия в разметке, конечно, нужна функция формирования адреса.
1
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,076
05.07.2020, 19:01  [ТС]
Цитата Сообщение от sad67man Посмотреть сообщение
Главное чтоб была понятна основная идея.
Спасибо, основная идея понятна. И ведь знаю, что логику нужно максимально отделять от представления...
Кое что не работало, изменил. Кое-что переделал по своему с учетом замечаний и образца. Отдельное спасибо за "напоминание" про функции min / max. Меньше кода, чем с условиями и более удобно читается, чем с if / else. Как раз в голове вертелось, как сделать такую хитроумную проверки, а сам не вспомнил, пока не увидел пример..

Цитата Сообщение от estic Посмотреть сообщение
Это опция для верстальщика. Соответственно можно в шаблоне "захардкодить".
Спасибо, попробую учитывать в будущем.

Цитата Сообщение от estic Посмотреть сообщение
stop как-то непривычно звучит; наверное, это семантически не самое точное слово
в контексте ассоциации процесса цикла думал - начало цикла и остановка. Согласен, что end более уместно.

Цитата Сообщение от estic Посмотреть сообщение
Для "широкой" строки навигации, позволяющей переключаться не только на соседние страницы (и "крайние" страницы)
Вот это как раз таки основной момент, который занял/занимает у меня наибольшее время - куда затолкать "функциональные" "кнопки" пагинатора : вперед,назад,первая,последняя.

Если использовать. как у меня выше - многоточия и затолкать их в массив к адресам пагинатора, то в шаблоне при выводе ссылок нужно будет в нескольких местах делать проверку, является ли например элемент массива объектом или нет, чобы не применять к строковому элементу многоточия поверку на то, является ли элемент текущим. Со стрелочками назад/вперед - пока тупик - как их передавать в массиве объектов пагинатора..

Или их жестко прописывать в представлении. Но это опять же куча обращений к объекту. Сделать у объектов свойства next/prev/first/last?

Вопрос не в конкретной реализации а в подходе

Добавлено через 1 час 54 минуты
Промежуточный полуфабрикат в контексте многоточий по краям пагинатора для тех значений, которые вне поля зрения:
В самом пагинаторе:
Кликните здесь для просмотра всего текста

PHP
1
2
3
4
5
6
7
8
9
10
$pages = [];
        if ($start >= $this->wingSize) {
            $pages[] = '...';
        }
        for ($i = $start; $i <= $end; $i++) {
            $pages[] = new PaginationItem($i, $i == $this->currentPage);
        }
        if ($this->currentPage < $this->getPagesCount() - $this->wingSize) {
            $pages[] = '...';
        }


В шаблоне:
Кликните здесь для просмотра всего текста
HTML5
1
2
3
4
5
6
7
8
9
10
11
<?php foreach ($pagination->getPaginatorLinksArray() as $page): ?>
        <?php if (!is_object($page)):?>
            <?=$page?>
        <?php continue; ?>
        <?php endif ?>
            <?php if ($page->isCurrent):?>
                <b>[<?= $page->number ?>]</b>
            <?php else: ?>
                <a href="<?= '/blog/' . $page->number ?>">[<?= $page->number ?>]</a>
            <?php endif ?>
    <?php endforeach ?>
0
1306 / 998 / 232
Регистрация: 01.10.2018
Сообщений: 3,882
05.07.2020, 19:04
Цитата Сообщение от СергейСереб Посмотреть сообщение
Отдельное спасибо за "напоминание" про функции min / max. Меньше кода, чем с условиями и более удобно читается, чем с if / else. Как раз в голове вертелось, как сделать такую хитроумную проверки, а сам не вспомнил, пока не увидел пример..
Да, в этих формулах используются min/max или тернарный оператор, но в тернарном приходится дублировать выражение из условия (представляющее одно из двух сравниваемых в условии значений), поэтому min/max нагляднее. Вот те же самые формулы, записанные при помощи тернарного оператора, из одного нашего блога, чтобы могли сравнить: https://gency.ru/comment/31

Цитата Сообщение от СергейСереб Посмотреть сообщение
Вот это как раз таки основной момент, который занял/занимает у меня наибольшее время - куда затолкать "функциональные" "кнопки" пагинатора : вперед,назад,первая,последняя.
В шаблон, конечно. Это же разметка. Хотя встречаются "специалисты", которые целиком строку навигации (готовую разметку) возвращают из "пагинатора", но вы на них не равняйтесь Вот пример "узкой" строки навигации из шаблона этого демонстрационного сайта:
PHP
1
2
3
4
5
6
7
8
9
10
11
12
<div class="pagination">
<?php if ($pn<$pc): ?>
  <a class="pagination-item older" href="<?= pagelink($pn+1) ?>">Older</a>
<?php else: ?>
  <span class="pagination-item older">Older</span>
<?php endif; ?>
<?php if ($pn>1): ?>
  <a class="pagination-item newer" href="<?= pagelink($pn-1) ?>">Newer</a>
<?php else: ?>
  <span class="pagination-item newer">Newer</span>
<?php endif; ?>
</div>
Ссылки для первой и последней страниц добавляются точно так же. И "широкая" строка делается так же: видел ее код в следующем комментарии за показанным по ссылке выше, причем в тех же "терминах", которые использовал я в показанном выше коде. Только не смотрите на контроллер из статьи. Он очень "толстый", хоть и без вызова "рендера". Как я писал выше, first (start) и last (end) по конкретному значению "range" можно получить прямо в шаблоне при помощи одной или пары функций, а контроллер здесь простейший. Вот с демонстрационного сайта (в тех же "терминах" и тоже без "рендера"):
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//try
//{
    $pc=pagecount('posts',$site['paginate']);
 
    rotate01();
    if ($pn==0||$pn>$pc)
    {
        error(404);
        return;
    }
 
    $result=collection('posts',$pn,$site['paginate'],'`post` DESC');
//}
//catch (Exception $e) { error(503); }
Это все (Перехват исключения можно вынести во фронт-контроллер.)

Добавлено через 3 минуты
Ну, вы еще можете профильтровать параметр $pn. У меня он стандартный (устанавливается и фильтруется автоматически).
1
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,076
05.07.2020, 19:06  [ТС]
del
0
 Аватар для wwowa
308 / 261 / 70
Регистрация: 01.12.2008
Сообщений: 1,031
05.07.2020, 19:44
Цитата Сообщение от estic Посмотреть сообщение
Хотя встречаются "специалисты", которые целиком строку навигации (готовую разметку) возвращают из "пагинатора", но вы на них не равняйтесь
Ну я один из них
У меня разметку навигации формируют аж семь переменных. И соответственно циклы и условия. И что же все это тащить в разметку? Когда в классе контроллера все под рукой - сформировал разметку и перекинул в шаблон одной переменной.
В результате в шаблоне
HTML5
1
  {{ @pag | RAW }}
И что так совсем плохо делать????
0
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,076
05.07.2020, 22:21  [ТС]
Спасибо. Почитал про варианты пагинаторов, есть что поизучать, над чем подумать, но это как мне кажется уже - нюансы, нужно подвигаться дальше. С многоточиями пока осталось все-таки под вопросом, остановился на таком полуфабрикате шаблона пагинатора:
Кликните здесь для просмотра всего текста

HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<p>
    <a href="/blog/1"><<&nbsp</a>
    <a href="/blog/<?=$pagination->getPrev()?>"><&nbsp</a>
    <?php foreach ($pagination->getPaginatorLinksArray() as $page): ?>
        <?php if (!is_object($page)):?>
            <?=$page?>
        <?php continue; ?>
        <?php endif ?>
            <?php if ($page->isCurrent):?>
                <b>[<?= $page->number ?>]</b>
            <?php else: ?>
                <a href="<?= '/blog/' . $page->number ?>">[<?= $page->number ?>]</a>
            <?php endif ?>
    <?php endforeach ?>
    <a href="/blog/<?=$pagination->getNext()?>">&nbsp></a>
    <a href="/blog/<?=$pagination->getLast()?>">&nbsp>></a>
</p>
0
1306 / 998 / 232
Регистрация: 01.10.2018
Сообщений: 3,882
06.07.2020, 09:20
Цитата Сообщение от wwowa Посмотреть сообщение
И что так совсем плохо делать?
В том виде, как вы это описали, делать не желательно, даже если вы являетесь программистом, верстальщиком и т.д. в одном лице.

При этом показанная вставка вполне допустима, если формировать значение переменной на основе файла шаблона или непосредственно функции представления, хотя использовать функцию представления для построения "больших" блоков - не самый лучший вариант, а то быстро получится WP

В общем же достаточно include в шаблоне, если вы хотите повторно использовать этот блок, например:
PHP
1
<?php include __DIR__.'/includes/pagination.php'; ?>
или
Code
1
{% include pagination.html %}
1
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,076
06.07.2020, 14:41  [ТС]
Цитата Сообщение от sad67man Посмотреть сообщение
PHP
1
2
3
4
5
6
for($i = $start; $i <= $end; $i++) {
            $pages[] = new PaginationItem(
                $i,
                $i === $this->getCurrentPage()
            );
        }
Относительно этого цикла есть вопрос. Вернее поверки равенства $i === $this->getCurrentPage()
Допустим, у меня 11 страниц. Начиная со страницы 9 текущие страницы перестают выделяться. хотя как ссылки - активные и работают. Не работает само выделение текущей страницы. Если делать поверку на не строгое соответствие все работает. Что-то я пока никак не въеду в чем причина. Сама проверка на строгое соответствие, как я понимаю и не нужна, достаточно обычного сравнения.
0
 Аватар для sad67man
2603 / 1507 / 689
Регистрация: 23.08.2015
Сообщений: 3,823
06.07.2020, 15:31
СергейСереб,
Если строгое сравнение не работает, значит вы сравниваете integer со string, то есть у вас currentPage не приведен к инту. Возможно вы убрали типизацию из конструктора или еще что-то. Надо смотреть ваш класс.

Добавлено через 10 минут
СергейСереб, С многоточиями конечно выглядит костыльно, метод должен возвращать массив однотипных данных. Вы можете добавить отдельные методы
PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php if (!$pagination->isStart()): ?>
    <span>...</span>
<?php endif ?>
<?php foreach ($pagination->getPaginatorLinksArray() as $page): ?>
    <?php if ($page->isCurrent):?>
        <b>[<?= $page->number ?>]</b>
    <?php else: ?>
        <a href="<?= '/blog/' . $page->number ?>">[<?= $page->number ?>]</a>
    <?php endif ?>
<?php endforeach ?>
<?php if (!$pagination->isEnd()): ?>
    <span>...</span>
<?php endif ?>
1
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,076
06.07.2020, 21:11  [ТС]
Цитата Сообщение от sad67man Посмотреть сообщение
Если строгое сравнение не работает, значит вы сравниваете integer со string, то есть у вас currentPage не приведен к инту
Везде вроде есть где нужно приведение типов. При сравнении в цикле $currentPage с $i сделал поверку типов и оказалось, что текущая страница то как раз целочисленного типа постоянно, а вот когда она равна 9 и до конца страниц, то $i у меня становится типом float. Этот тип приходит раньше, от переменной $start. Почему так получается, откуда там float - пока что не могу разобраться, вроде все проверил.

Такая же проблема при сравнении происходит в методе getNext(). И так же начиная с текущей страницы = 9 и до последней страницы
9 = общее количество страниц минус размер крыла(количество показываемых страниц от текущей).
Если сравнение не строгое, то все работает.
В методе getPrev() тоже строгое сравнение, но там все работает как положено.
Какое то магическое числo 9...
Класс пагинатора:
Кликните здесь для просмотра всего текста

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<?php
 
 
namespace MyProject\Services;
 
/**
 * Class paginator
 * @package MyProject\Services
 */
class Paginator
{
    /**
     * @var int
     */
    private $wingSize = 2;
    /**
     * @var int
     */
    private $currentPage;
    /**
     * @var int
     */
    private $articlesInDb;
 
    /**
     * @var int
     */
    private $limit = 3;
 
    /**
     * paginator constructor.
     * @param int $currentPage
     * @param int $articlesInDb
     */
    public function __construct(int $currentPage, int $articlesInDb)
    {
        $this->currentPage = $currentPage;
        $this->articlesInDb = $articlesInDb;
    }
 
    /**
     * @return int
     */
    public function getPagesCount()
    {
        return ceil($this->articlesInDb / $this->limit);
    }
 
    /**
     * @return int
     */
    public function getPrev()
    {
        return $this->getCurrentPage() === 1 ? 1 : $this->getCurrentPage() - 1;
    }
 
    /**
     * @return int
     */
    public function getLast()
    {
        return $this->getPagesCount();
    }
 
    /**
     * @return int
     */
    public function getNext()
    {
        return $this->getCurrentPage() === $this->getPagesCount() ? $this->getPagesCount() : $this->getCurrentPage() + 1;
    }
 
    /**
     * @return int
     */
    public function getCurrentPage()
    {
        return $this->currentPage;
    }
    
    /**
     * @return int
     */
    public function getPagesLimit()
    {
        return $this->limit;
    }
 
    /**
     * @return int
     */
    public function getOffset()
    {
        return ((max(1, $this->getCurrentPage())) - 1) * $this->getPagesLimit();
    }
 
    /**
     * @return bool
     */
    public function isStart(): bool
    {
        return $this->getCurrentPage() < $this->wingSize * 2;
    }
 
    /**
     * @return bool
     */
    public function isEnd(): bool
    {
        return $this->getCurrentPage() >= $this->getPagesCount() - $this->wingSize;
    }
 
    /**
     * @return array
     */
    public function getPaginatorLinksArray()
    {
        $showedPagesCount = $this->wingSize * 2 + 1;
        $start = max(1, $this->getCurrentPage() - $this->wingSize);
        $end = min($this->getPagesCount(), $start + $showedPagesCount - 1);
        $start = max(1, $end - ($showedPagesCount - 1));
        $pages = [];
        for ($i = $start; $i <= $end; $i++) {
            $pages[] = new PaginationItem($i, (int)$i === $this->getCurrentPage());
        }
        return $pages;
    }
}
0
 Аватар для sad67man
2603 / 1507 / 689
Регистрация: 23.08.2015
Сообщений: 3,823
06.07.2020, 21:33
СергейСереб, float возникает при делении.
PHP
1
2
3
4
public function getPagesCount()
{
    return ceil($this->articlesInDb / $this->limit);
}
Если указать возвращаемый тип, тогда он будет приводиться к инту
PHP
1
2
3
4
public function getPagesCount(): int
{
    ...
}
Можно и оставить нестрогое сравнение.
1
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,076
06.07.2020, 21:45  [ТС]
Цитата Сообщение от sad67man Посмотреть сообщение
float возникает при делении
Опять невнимательность меня подводит...
Я ведь был уверен, что раз ceil округляет до целого числа, то и возвращает целое число в формате int. Да и док-блок сбил с толку. Нет чтобы порверить в документации...
Cпасибо.
0
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,076
07.07.2020, 13:22  [ТС]
Пока доделывал/переделывал, вспомнил такой нюанс - Насколько правильно/неправильно, принципиально/не принципиально использование разных способов работы со свойствами объекта в самом классе или в представлении? А именно, можно напрямую обратиться к свойству, а можно через геттер/сеттер. Если свойство защищенное, приватное, то понятное дело, а кроме этого есть еще ограничения если свойство публичное? Каких-то стандартов по этому поводу не видел.
0
1306 / 998 / 232
Регистрация: 01.10.2018
Сообщений: 3,882
07.07.2020, 14:10
Цитата Сообщение от СергейСереб Посмотреть сообщение
А именно, можно напрямую обратиться к свойству, а можно через геттер/сеттер.
Это не принципиально. Для объекта представления можно и напрямую к свойствам обращаться. У вас там другая проблема, о чем я уже писал. Показанный "пагинатор" слишком сложен для верстальщика. Нужно его приводить к более типовому для подобных объектов виду. Возможно, объединять с основным объектом представления для этих страниц.

Добавлено через 7 минут
Условия наподобие !$pagination->isEnd() делаются гораздо проще: $pn<$pc (текущая страница сравнивается с общим количеством страниц, т.е. с номером последней). По сути для построения строки навигации на данном этапе нужны только pn, pc и, возможно, count. Для "широкой" строки - также start/end и параметр для их вычисления "range", который в общем-то можно инкапсулировать в объект или функции для получения start/end. Все остальное делается вполне понятными верстальщику сравнениями
0
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,076
07.07.2020, 14:13  [ТС]
Цитата Сообщение от estic Посмотреть сообщение
Показанный "пагинатор" слишком сложен для верстальщика. Нужно его приводить к более типовому для подобных объектов виду.
Не знаю, как еще упростить..
На сейчас, это выглядит так:

Кликните здесь для просмотра всего текста

HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<p>
    <a href="/blog/1"><<&nbsp</a>
    <a href="/blog/<?=$pagination->getPrev()?>"><&nbsp</a>
    <?php if (!$pagination->isStart()): ?>
    <span>...</span>
    <?php endif ?>
    <?php foreach ($pagination->getPaginatorLinksArray() as $page): ?>
            <?php if ($page->isCurrent):?>
                <b><?= $page->number ?></b>
            <?php else: ?>
                <a href="<?= '/blog/' . $page->number ?>"><?= $page->number ?></a>
            <?php endif ?>
    <?php endforeach ?>
    <?php if (!$pagination->isEnd()): ?>
    <span>...</span>
    <?php endif ?>
    <a href="/blog/<?=$pagination->getNext()?>">&nbsp></a>
    <a href="/blog/<?=$pagination->getLast()?>">&nbsp>></a>
</p>


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

Цитата Сообщение от estic Посмотреть сообщение
текущая страница сравнивается с общим количеством страниц, т.е. с номером последней
Эти параметры нужно через геттеры получить, т.к. они приватные. не знаю, будет ли это в том же контексте упрощать жизнь верстальщику. Мне кажется, isEnd, isStart более интуитивно понятно.
И не сработает у меня такая проверка в моем алгоритме вывода номеров страниц, я уже такой вариант проходил.

Ну и опять же, я пока на нюансах не хотел бы останавливаться. Оставляю это на потом.
0
1306 / 998 / 232
Регистрация: 01.10.2018
Сообщений: 3,882
07.07.2020, 14:39
Я прежде всего имел в виду сам объект. Сложный код строки навигации - это уже следствие. Даже методы getPrev/getNext - это уже лишняя для него информация. Неужели вы считаете, что он не сможет написать $pn-1/$pn+1?

В общем вы путаете себя, как всюду использующего объекты программиста, с верстальщиком. Если здесь все же использовать объекты, верстальщикам в радость такие конструкции: $page->number - 1, $page->number + 1, $page->number < $page->count и т.п.
1
39 / 9 / 10
Регистрация: 19.09.2016
Сообщений: 1,076
07.07.2020, 14:42  [ТС]
Цитата Сообщение от estic Посмотреть сообщение
Если здесь все же использовать объекты, верстальщикам в радость такие конструкции: $page->number - 1, $page->number + 1, $page->number < $page->count и т.п.
Ок. спасибо. Приму во внимание
0
1306 / 998 / 232
Регистрация: 01.10.2018
Сообщений: 3,882
07.07.2020, 15:30
Цитата Сообщение от СергейСереб Посмотреть сообщение
href="/blog/1"
getFirst забыли Не заставляйте верстальщика провоцировать дубли И про "/blog/" в шаблоне уже писал. Если не можете скрыть формирование адреса в специальной функции, делайте хотя бы так:
PHP
1
href="<?= BLOG_URL, $pagination->getFirst() ?>"
Добавлено через 3 минуты
Хотя, как вы писали в первом сообщении, там путь тоже вариативный, поэтому константу, наверное, лучше не использовать.

Добавлено через 9 минут
Цитата Сообщение от estic Посмотреть сообщение
делайте хотя бы так
Правда, на повторное использование кода с разметкой в этом случае уже трудно рассчитывать.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
07.07.2020, 15:30
Помогаю со студенческими работами здесь

Неправильно выводит количесвто страниц в пагинации
Здравствуйте! Собственно сама проблема в названии топика, никак не могу понять почему. function...

Как прописать условие,чтоб не было лишних ссылок пагинации?
Сделал скрипт пагинации.Но не могу придумать как прописать так чтоб когда заканчивались записи в базе данных то больше ссылок на страниц...

Создание пагинации
Здравствуйте,вот ниже мой код что читает з бд инфу и выкидывает на страницу,это ясно ,но выкидывает все записи их около 20-ти больше и не...

Улучшение моей пагинации
Сделал очень костыльную пагинацию, приходится каждый раз писать условия и создавать самому страницы, как можно это автоматизировать? ...

Изменение формы пагинации на JS
Здравствуйте. Помогите пожалуйста переписать мою форму пагинации. Сейчас она имеет вид: 123456789101112131415 и т.д. ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
http://iceja.net/ математические сервисы
iceja 20.01.2026
Обновила свой сайт http:/ / iceja. net/ , приделала Fast Fourier Transform экстраполяцию сигналов. Однако предсказывает далеко не каждый сигнал (см ограничения http:/ / iceja. net/ fourier/ docs ). Также. . .
http://iceja.net/ сервер решения полиномов
iceja 18.01.2026
Выкатила http:/ / iceja. net/ сервер решения полиномов (находит действительные корни полиномов методом Штурма). На сайте документация по API, но скажу прямо VPS слабенький и 200 000 полиномов. . .
Расчёт переходных процессов в цепи постоянного тока
igorrr37 16.01.2026
/ * Дана цепь постоянного тока с R, L, C, k(ключ), U, E, J. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа, решает её и находит переходные токи и напряжения на элементах схемы. . . .
Восстановить юзерскрипты Greasemonkey из бэкапа браузера
damix 15.01.2026
Если восстановить из бэкапа профиль Firefox после переустановки винды, то список юзерскриптов в Greasemonkey будет пустым. Но восстановить их можно так. Для этого понадобится консольная утилита. . .
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru