Zum Inhalt springen

tuerchen 02 · 2. Dezember

Seite 1 von irgendwas

Bug fixenLeichtPHP 8.4

Aufgabe

Das Wunschzettel-Archiv der Wichtelzentrale blättert seitenweise. Seit dem letzten Refactoring kommen Beschwerden: Auf Seite 1 stehen die Wünsche von Seite 2, die letzte Seite fehlt und die Wunsch-IDs sind plötzlich 0, 1, 2 …

Repariere paginate(array $items, int $page, int $perPage): array. So soll sie arbeiten:

  • $page beginnt bei 1. $page < 1 oder $perPage < 1 → InvalidArgumentException (das klappt schon).
  • items: die Einträge der Seite, mit ihren ursprünglichen Schlüsseln (auch Integer-Schlüssel wie Wunsch-IDs).
  • pages: Anzahl der Seiten insgesamt. Eine angefangene Seite zählt mit, ein leeres Archiv hat 0 Seiten.
  • has_next: ob nach dieser Seite noch eine kommt.
  • Eine Seite hinter dem Ende liefert items = [] (keine Ausnahme).

Sichtbare Tests

  • Seite 1 von 11 Einträgen

    echo json_encode(paginate(range(1, 11), 1, 5));

    Erwartet: {"items":[1,2,3,4,5],"page":1,"pages":3,"has_next":true}

  • Angefangene letzte Seite

    echo json_encode(paginate(range(1, 11), 3, 5));

    Erwartet: {"items":{"10":11},"page":3,"pages":3,"has_next":false}

  • Seite 0 ist ungültig

    try {
        paginate([1, 2], 0, 5);
        echo 'keine Ausnahme';
    } catch (InvalidArgumentException $e) {
        echo 'InvalidArgumentException';
    }

    Erwartet: InvalidArgumentException

Dazu kommen 8 versteckte Tests, die nur beim Abgeben laufen.

Tipp anzeigenkostet keine Punkte

array_slice() zählt ab 0, deine Seiten ab 1. Schau dir außerdem den vierten Parameter von array_slice() an.

Extra-Stufe (+5 Punkte, freiwillig)

Extra-Stufe (+5): Negative Seitenzahlen zählen von hinten, wie bei array_slice(): -1 ist die letzte Seite, -2 die vorletzte. page im Ergebnis ist dann die aufgelöste positive Seitenzahl. Liegt eine negative Seite vor der ersten (z. B. -4 bei 3 Seiten), gibt es items = [], has_next = false und als page die aufgelöste Zahl (hier 0). 0 bleibt ungültig.

Kein zweiter Editor: Dein Code oben zählt für beides. Die Extra-Tests laufen mit jeder Abgabe (5 Tests).

Mitraten geht mit deinem kostenlosen Entwicklerkonto

Lesen darf jeder. Punkte, Bestenliste und Preise gibt es nur mit Konto.

Auflösung

Drei klassische Fehler auf einmal:

  1. Offset: Seiten zählen ab 1, array_slice() ab 0. Der Start ist ($page - 1) * $perPage.
  2. Seitenzahl: intdiv() rundet ab; 11 Einträge à 5 sind 3 Seiten, nicht 2. Richtig: (int) ceil(count($items) / $perPage).
  3. Schlüssel: array_slice() nummeriert Integer-Schlüssel neu durch, String-Schlüssel bleiben. Erst der vierte Parameter preserve_keys: true behält die IDs.

has_next war übrigens richtig – es wirkte nur falsch, weil pages falsch war. Siehe array_slice.

Musterlösung

<?php

/**
 * Liefert eine Seite des Wunschzettel-Archivs.
 *
 * @return array{items: array, page: int, pages: int, has_next: bool}
 */
function paginate(array $items, int $page, int $perPage): array
{
    if ($page === 0 || $perPage < 1) {
        throw new InvalidArgumentException('page darf nicht 0 sein, perPage muss mindestens 1 sein.');
    }

    $pages = (int) ceil(count($items) / $perPage);
    if ($page < 0) {
        $page = $pages + 1 + $page; // -1 = letzte Seite
    }
    if ($page < 1) {
        return ['items' => [], 'page' => $page, 'pages' => $pages, 'has_next' => false];
    }

    return [
        'items' => array_slice($items, ($page - 1) * $perPage, $perPage, true),
        'page' => $page,
        'pages' => $pages,
        'has_next' => $page < $pages,
    ];
}

Zurück zum Kalender