Znalezienie odpowiedniego przedziału

0

Witam!

Pracuję nad __uniwersalną __funkcją, która znajdzie mi odpowiedni przedział dla liczb. W późniejszym etapie chcę wykorzystać ją do paginacji.

function get_id_start($num_items, $id)
{
    $board_config['adverts_per_page'] = 10; // ile wyników na stronę
    
    $id = intval($id);
    
    if ( $id <= 0 )
    {
        return 0;
    }
    
    $total_pages = ceil($num_items/$board_config['adverts_per_page']); // ile będzie wszystkich stron
    $floor_num = $num_items; // wszystkich wyników do wyświetlenia
    $floor_num += (10-($floor_num%10)); // zaokrąglamy w górę wszystkie wyniki - do dziesiątek, czyli liczba 31 jest równa 40 itd.
    
    for($i=0; $i<=($total_pages-1); $i++)
    {
        $start = $floor_num-(($i+1)*$board_config['adverts_per_page']);
        $end = $floor_num-($i*$board_config['adverts_per_page']);
                    
        if ( $id >= $start && $id <= $end )
        {
            return $start;
        }
        else if ( $i == 0 && $id > $end )
        {
            return $start;
        }
    }
}

var_dump(get_id_start(31, 7));

Powyższa funkcja ma znaleźć przedział dla liczby 7, gdzie wszystkich wyników do wyświetlenia w sumie jest 31, a wyników na stronę ma być 10.

Pętla for zwraca coś mniej więcej takiego:
30 -> 40
20 -> 30
10 -> 20
0 -> 10

Czyli ta nasza siódemka mieści się w przedziale 0 - 10 (bo jest mniejsza od 10 i większa od 0).

Problem zaczyna się ze wspomnianą uniwersalnością... Próbując znaleźć przedział dla liczby 7, gdzie wszystkich wyników do wyświetlenia mamy 31, a wyników na stronę 3 (tutaj zmiana względem powyższego przykładu), wychodzą mi jakieś totalne głupoty. Mam funkcję, która dla tej trójki znajduje wszystkie przedziały poprawnie:

function get_id_start_pagination($num_items, $id)
{
    $board_config['adverts_per_page'] = 3;
    
    $id = intval($id);
    
    if ( $id <= 0 )
    {
        return 0;
    }
    
    $total_pages = ceil($num_items/$board_config['adverts_per_page']);
    
    for($i=0; $i<=($total_pages-1); $i++)
    {
        $start = $num_items-(($i+1)*$board_config['adverts_per_page']);
        $end = $num_items-($i*$board_config['adverts_per_page']);
        
        echo $start.' --> '.$end.'<br>';
                    
        if ( $id >= $start && $id <= $end )
        {
            return $start;
        }
        else if ( $i == 0 && $id > $end )
        {
            return $start;
        }
    }
}

var_dump(get_id_start_pagination(31, 7));

Funkcja zwróci przedziały:

28 --> 31
25 --> 28
22 --> 25
19 --> 22
16 --> 19
13 --> 16
10 --> 13
7 --> 10
4 --> 7
1 --> 4

a więc siódemka wchodzi w przedział 4 - 7.
Obie funkcje nie współpracują ze sobą, wypluwając błędnę wartości dla innych argumentów niż zaprezentowane przeze mnie powyżej.
Czy ktoś by mi mógł pomóc z tą matematyką, tak aby stworzyć jedną funkcję, która będzie uniwersalna (czyt. wypluwała dobre wartości, niezależnie od argumentów)? :)

1

Witaj,

Odpowiedź trochę nie na czasie, ale żadnej nie dostałeś, więc - jeśli sobie sam nie poradziłeś - może Ci się przydać.
Zakładam, że chodzi Ci o wyliczenie numeru strony, na której pojawi się ogłoszenie z danym ID.
Jeśli tak, to na przykładzie Twojej funkcji powinno to wyglądać mniej więcej tak:


function getPageNumberForId($numItems = 0, $id = 0, $advertsPerPage = 3) {
    $id = intval(0+$id);
 
    if ($numItems==0 || $id==0) {
        return 1; // return first page
    }
 
    $totalPages = ceil($numItems/$advertsPerPage);
 
    for ($i=$numItems; $i>0; $i-=$advertsPerPage) {
        $page = $totalPages-ceil($i/$advertsPerPage)+1; // page number
        $first = $i; // first advert for page
        $last = $i-$advertsPerPage+1; // last advert for page
        $last = $last>1 ? $last : 1;

        echo 'page '.$page.': '.$first.'...'.$last.'<br/>'; // testing only
 
        if ($id>=$last && $id<=$first) {
            return $page;
        }
    }
}

Powyższe jednak jest wielce nieefektywne, ponieważ w przypadku wielu elementów znalezienie numeru strony dla odległego ogłoszenia (z niskim ID) zajmie zauważalnie dużo czasu. Przykładowo:

var_dump(getPageNumberForId(100000, 743, 10));

Lepiej będzie napisać tę funkcję tak:


function getPageNumberForIdNoLoop($numItems = 0, $id = 0, $advertsPerPage = 3) {
    $id = intval(0+$id);
 
    if ($numItems==0 || $id==0) {
        return 1; // return first page
    }
 
    $numSkip = $numItems-$id; // number of ads to skip
    $page = floor($numSkip/$advertsPerPage)+1; // page number
    $first = $numItems-($page-1)*$advertsPerPage; // first advert for page
    $last = $numItems-$page*$advertsPerPage+1; // last advert for page
    $last = $last>1 ? $last : 1;

    echo 'page '.$page.': '.$first.'...'.$last.'<br/>'; // testing only

    return $page;
}
 
var_dump(getPageNumberForIdNoLoop(100000, 743, 10));

Przetestuj oba warianty, a gołym okiem zobaczysz różnicę w czasie ich wykonywania.

Tak, czy inaczej funkcja ta (bez pętli) spełni swoje zadanie jedynie w przypadku, gdy ogłoszenia nie będą kasowane/ukrywane/etc:

  • usunięcie przykładowo 15 ogłoszeń z przedziału <800;900> sprawi, że użytkownik nie trafi na właściwą stronę.

Gdybyś zdecydował się na usuwanie/ukrywanie/etc. ogłoszeń, co moim zdaniem jest sprawą raczej konieczną, to do zmiennej $numSkip trzeba będzie przypisać wartość zwróconą przez zapytanie do bazy danych o ilość widocznych ogłoszeń z ID większym od szukanego. Przykładowe zapytanie:

$query = 'SELECT COUNT(*) AS numSkip FROM table WHERE id>'.$id.' AND published=1 AND deleted=0 AND visible=1';

Mam nadzieję, że choć trochę pomogłem.

1 użytkowników online, w tym zalogowanych: 0, gości: 1