Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [php]Bez użycia konstrukcji warunkowych
Forum PHP.pl > Forum > Przedszkole
sadistic_son
Cześć, zmagam się z pewnym zadaniem, ze z góry narzuconymi regułami. W skrócie to mały, prosty sklepik internetowy, który w całości ma powstać obiektowo. Programowanie proceduralne dozwolone jest tylko do zainicjowania klas. I jedno z wymagań mówi aby unikać konstrukcji warunkowych (if-else oraz switch) do radzenia sobie z różnicami w produktach. Jako że obiektowo programować dopiero się uczę, to nie wiem czy jest może jakieś oczywiste rozwiązanie pozwalające na nieużywanie warunków.

Załózmy, że mam w bazie tabelę PRODUKT z takimi kolumnami oraz danymi:
Kod
ID | name | typ|  MB | ilosc_stron | szerokosc | wysokosc | glebokosc

1 | tetris | dvd | 128 | 0 | 0 | 0 | 0
2 | bilbia | ksiazka | 0 | 1256 | 0 | 0 | 0
3 | stol | mebel | 0 | 0 | 90 | 200 | 60

Jak wdać mam 3 typy produktów (DVD, ksiazka, mebel) i w zależności od tego jaki to typ to odpowiednie kolumny są wypełnione - MB dla dvd, ilosc_stron dla ksiazki i wymiary dla mebli. Zresztą w oddzielnym temacie pytam czy dobrze rozumuję, że tak właśnie powinna baza pod ten cel wyglądać.
W każdym razie jak teraz rozróżnić inne akcje do działania na książce, dvd czy meblu kiedy nie mogę zastosować if-else czy switch ?

Dzięki.
nospor

Co do bazy to jak pisalem w innym temacie jest zla

no ale wracajac do pytania, to jak najbardziej mozna nie uzywac akruat tutaj IF czy switch. Wystarczy stworzyc tabele w php mowiaca, dla jakiego typu wypelnic jakie tabele

  1. $types = ['dvd' => ['MB'], 'meble' => ['wysokosc', 'szerokosc'], 'ksiazka' => ['ilosc stron']];

I teraz majac typ, pobhierasz z tablicy pola ktore cie interesuja
  1. $types[$type]

I juz.
sadistic_son
Odkopujęsmile.gif
Ileż ja się naszukałem tego tematu - 3 strony przedszkola przeczesałem ze 4 razy, zanim sobie przypomniałem, że tutaj (PHP|OOP) jest ten post.


@nospor - czyli coś w tym stylu (pisane na szybko z palca) to powinno być?
  1. $types = array('book' => 'weight', 'dvd' => 'size', 'furniture' => array('width', 'height', 'lenght'));
  2.  
  3. foreach ($types as $type) {
  4. $query = "INSERT INTO 'product' (id, name, type) VALUES (null, '$name' , '$type')";
  5. //query execute itd...
  6. }
O to ci chodzi?

EDIT: Nie no, bez sensu. TO co dałem powyżej to jest źle. Chwila, musze pomyśleć....
nospor
Nie
Zdaje sie masz leciec tylko po tym co podal w select xz forma
Nazewnictwo pozatym jak zwykle do 4 liter....


foreach ($types['select z forma'] as $wlasciwosci) {

I tu petla po $wlasciwosci i sobie pobierasz z POST te wlasciwosci
}

I przenosze. Jakic cudem to na OOP siedzi jeszcze
sadistic_son
Dobra, a więc uaktualniając temat, to baza wygląda tak:
Kod
products:
id | name | type
1 | Krzyzacy | book
2 | Rambo | dvd
3 | Table | furniture

property:
id | type | propoerty
1 | book | weight
2 | dvd |size
3 | furniture | height
4 | furniture | width
5 | furniture | lenght

product_propoerty:
id | product_id | propoerty_id | value
1 | 1 | 1 | 0.5 (Krzyzacy | weight | 0.5 )
2 | 2 | 2 | 650 (Rambo | size | 650 )
3 | 3 | 3 | 60 (Table | height | 60)
4 | 3 | 4 | 90 (Table | width | 90)
5 | 3 | 5 | 180 (Table | lenght | 180)


I teraz takie coś:
  1. <?php
  2. $name = $_POST['name'];
  3. $selectedType = $_POST['selectedType'];
  4. $givenProperties = array();
  5. if (isset($_POST['size']))
  6. $givenProperties['size'] = $_POST['size'];
  7. if (isset($_POST['weight']))
  8. $givenProperties['weight'] = $_POST['weight'];
  9. if (isset($_POST['width']))
  10. $givenProperties['width'] = $_POST['width'];
  11. if (isset($_POST['height']))
  12. $givenProperties['height'] = $_POST['height'];
  13. if (isset($_POST['length']))
  14. $givenProperties['length'] = $_POST['length'];
  15.  
  16. $types = array('book' => 'weight', 'dvd' => 'size', 'furniture' => array('width', 'height', 'lenght'));
  17.  
  18. $queryInsertProduct = "INSERT INTO 'product' (id, name, type) VALUES (null, '$name' , '$typeIndex')";
  19. //query execute, results itd...
  20.  
  21. $lastProductId = $db->lastInsertId();
  22.  
  23. foreach ($types[$selectedType] as $typeIndex => $typeValue) {
  24. $queryPropertyType = "SELECT id FROM property WHERE type = $typeValue";
  25. //query execute, results itd...
  26. $propertyId = $result; //wyniki powyższego zapytania
  27. $query = "INSERT INTO 'product_property' (id, product_id, property_id, value) VALUES (null, $lastProductId , $propertyId, '$givenProperties[$selectedType]')";
  28. //query execute, results itd...
  29. }
Dobrze rozumuję nospor, biorąc pod uwagę twoje sugestie?
nospor
Nie, totalnei zle.


Zapomnij o tym
if (isset($_POST['size']))
$givenProperties['size'] = $_POST['size'];
if (isset($_POST['weight']))
$givenProperties['weight'] = $_POST['weight'];
if (isset($_POST['width']))
$givenProperties['width'] = $_POST['width'];
if (isset($_POST['height']))
$givenProperties['height'] = $_POST['height'];
if (isset($_POST['length']))
$givenProperties['length'] = $_POST['length'];

Przeciez ty nei wiesz ze jest property height, width size czy jakakowliek inna. Te dane sa w bazie.

A to
$types = array('book' => 'weight', 'dvd' => 'size', 'furniture' => array('width', 'height', 'lenght'));
porownaj sobie jak wygladalo zbudowane przeze mnie w pierwszym poscie. przeciez ja tam dalem wszedzie tablice, nawet na jeden element, a ty sobie radosnie tablice zamienies na teksty....
I jak juz porownasz to wowczas ta tablice $types ma byc zbudowana na podstawie danych w bazie.
sadistic_son
Wtakim razie tak?
  1. $name = $_POST['name'];
  2. $selectedType = $_POST['selectedType'];
  3. $types = ['dvd' => ['size'], 'furniture' => ['height', 'width', 'lenght'], 'book' => ['weight']];
  4.  
  5.  
  6. $queryInsertProduct = "INSERT INTO 'product' (id, name, type) VALUES (null, '$name' , '$selectedType')";
  7. //query execute, results itd...
  8.  
  9. $lastProductId = $db->lastInsertId();
  10.  
  11. foreach ($types[$selectedType] as $propertyIndex => $propertyValue) {
  12. $queryPropertyType = "SELECT id FROM property WHERE type = $propertyIndex";
  13. //query execute, results itd...
  14. $propertiesIds = $result; //wyniki powyższego zapytania
  15.  
  16. $query = "INSERT INTO 'product_property' (id, product_id, property_id, value) VALUES (null, $lastProductId , $propertyId, '$_POST[$propertyValue]')";
  17. //query execute, results itd...
  18. }


Cytat(nospor @ 9.01.2023, 13:25:19 ) *
I jak juz porownasz to wowczas ta tablice $types ma byc zbudowana na podstawie danych w bazie.
Yyyyy, muszę pomyśleć jak to zrobić.
nospor
Cytat
Yyyyy, muszę pomyśleć jak to zrobić.

Przeciez juz to robiles przynajmniej dwa razy. GRUPOWANIE DANYCH. TO jest dokladnie to co masz zrobic by zbudowac te tablice
sadistic_son
Tak, na to wpadłem, po fajeczce się za to zabiorę. A czy mój powyższy kod (pomijając tworzenie tablicy) to dobra droga?
Nie ma zabezpieczenia przed sytuacją gdzie user poda typ 'dvd' a do niego wagę oraz wysokość. Ale do tego wrócę jak już w ogóle wrzucanie do bazy będzie działać.
nospor
No ale ciagle jest zle bo cialge nie czytasz co do ciebie pisze oraz nei raczysz przenaluizowac.
Przeciez ci mowilem, ze w types masz tablice wlasciwosci a ty ciagle FOREACH piszesz jaby tam byl jeden element

nie
foreach ($types[$selectedType] as $propertyIndex => $propertyValue) {

a
foreach ($types[$selectedType] as $propertyIndex => $PROPERTIES) {
i $PROPERTIES to tablica wiec ma byc jescze jeden FOREASCH po PROPERTIES. Nie kaz mi tego pisac po raz 10ty prosze
sadistic_son
No dobra, dotarło. Czyli teraz tak...

To moja funkcja zwracająca pogrupowane properties:
  1. public function groupFullProperties(): array
  2. {
  3. $properties = array();
  4. foreach ($this->getFullProperties() as $row) {
  5. $property = $row['property'];
  6.  
  7. $newProperty = $row['type'];
  8. if (!isset($properties[$newProperty])) {
  9. $properties[$newProperty] = array();
  10. }
  11. $properties[$newProperty][] = [
  12. 'property' => $property,
  13. 'label' => $row['label'],
  14. 'description' => $row['description']
  15. ];
  16.  
  17. }
  18. return $properties;
  19. }
Ona zwraca tablicę wyglądającą tak:
Kod
Array
(
    [Book] => Array
        (
            [0] => Array
                (
                    [property] => weight
                    [label] => Weight (kg)
                    [description] => Please provide the weight of book in kg.
                )

        )

    [DVD] => Array
        (
            [0] => Array
                (
                    [property] => size
                    [label] => Size (MB)
                    [description] => Please provide size of DVD in MB.
                )

        )

    [Furniture] => Array
        (
            [0] => Array
                (
                    [property] => width
                    [label] => Width (cm)
                    [description] => Please provide dimentions in WxHxL format.
                )

            [1] => Array
                (
                    [property] => height
                    [label] => Height (cm)
                    [description] => Please provide dimentions in WxHxL format.
                )

            [2] => Array
                (
                    [property] => length
                    [label] => Length (cm)
                    [description] => Please provide dimentions in WxHxL format.
                )

        )

)
Ja stąd potrzebuję jedynie 'properties', czyli tablicę która będzie wyglądać tak:
  1. $types = ['dvd' => [0=>['property' => 'size']], 'furniture' => [0=>['property' => 'height'] , 1=>['property'=>'width'], 2=>['property'=>'lenght']], 'book' => [0=>['property'=>'weight']]];
Wygląda nieco inaczej niż to co podałeś, ale myślę, że warto użyć już istniejącej metody zamiast pisać nową, robiącą niemalże to samo (don't repeat yourself wink.gif).

Teraz kod realizujący dodawanie. Na razie bezczelnie wrzucam input od usera, bez czyszczenia POST, bez bindowania, itd.
  1. $db = new DB();
  2.  
  3. $name = $_POST['name'];
  4. $selectedType = $_POST['productType'];
  5.  
  6. $queryInsertProduct = "INSERT INTO `product` (`id`, `sku`, `name`, `price`, `type`) VALUES (null, '$_POST[sku]', '$name' , '$_POST[price]', '$selectedType')";
  7. $db->query($queryInsertProduct);
  8. $db->execute();
  9.  
  10. $lastProductId = $db->lastInsertId();
  11.  
  12. $properties = $this->groupFullProperties();
  13.  
  14. foreach ($properties[$selectedType] as $type => $properties) {
  15. foreach ($properties as $index => $propertyData) {
  16. if ($index == 'property') {
  17. $queryPropertyType = "SELECT `id` FROM `property` WHERE `property` = '$propertyData'";
  18. $db->query($queryPropertyType);
  19. $propertyId = $db->single();
  20. $postIndex = $propertyData;
  21. $queryInsertProductProperty = "INSERT INTO `product_property` (`id`, `product_id`, `property_id`, `value`) VALUES (null, $lastProductId , $propertyId[id], '$_POST[$postIndex]')";
  22. $db->query($queryInsertProductProperty);
  23. $db->execute();
  24. }
  25. }
  26.  
  27. }
No i teraz problem jest taki, że user może wpisać dane dla innego typu, np poda typ 'dvd' ale wpisze właściwości dla mebla (wysokość, szerokość). Tak więc produkt w takim przypadku nie powinien się w ogóle dodawać (pierwszy insert). Do pętli go wrzucić nie mogę, bo nie ważne ile jest właściwości properties, to produkt musi się dodać tylko raz. Jakieś sugestie jak z tego wybrnąć?
nospor
Dobra, widze ze chyba ja wczesniej pojechalem bez sensu..

Ale od poczatku

Do tych danych
[property] => width
                    [label] => Width (cm)
                    [description] => Please provide dimentions in WxHxL format.

Dodaj jeszcze property_id
Po grzyba masz potem w petli latac po to id, skoro mozesz je miec od razu

A potem petla:

  1. foreach ($properties[$selectedType] as $propertyData) {
  2. $propertyId = $propertyData['property_id'];
  3. $postIndex = $propertyData['property'];
  4. $queryInsertProductProperty = "INSERT INTO `product_property` (`id`, `product_id`, `property_id`, `value`) VALUES (null, $lastProductId , $propertyId, '$_POST[$postIndex]')";
  5. $db->query($queryInsertProductProperty);
  6. $db->execute();
  7.  
  8. }


Zainetersuj sie BINDowaniem bo przykro sie patrzy na te inserty wink.gif

Cytat
No i teraz problem jest taki, że user może wpisać dane dla innego typu, np poda typ 'dvd' ale wpisze właściwości dla mebla (wysokość, szerokość). Tak więc produkt w takim przypadku nie powinien się w ogóle dodawać (pierwszy insert). Do pętli go wrzucić nie mogę, bo nie ważne ile jest właściwości properties, to produkt musi się dodać tylko raz. Jakieś sugestie jak z tego wybrnąć?

No i co z tego ze wypelni inne pola? Ty z post w petli bierzesz tylko pola z danego typu a nie wszystkie
sadistic_son
Cytat(nospor @ 9.01.2023, 16:30:43 ) *
[property] => width
                    [label] => Width (cm)
                    [description] => Please provide dimentions in WxHxL format.

Dodaj jeszcze property_id
Po grzyba masz potem w petli latac po to id, skoro mozesz je miec od razu
No racja, bez sensu SELECTy mnożyć, skoro dane są na wyciągnięcie ręki. Dodane.


Pętelka działa cacy. Dzięki.

Cytat(nospor @ 9.01.2023, 16:30:43 ) *
Zainetersuj sie BINDowaniem bo przykro sie patrzy na te inserty wink.gif
Tak, mam już gdzieniegdzie nawet pokomentowane bindowanie zapytań. Ale jeszcze słabo to ogarniam (choć to nie jest nic nadzwyczajnego), więc jak kod będzie działać, to wtedy dodam bindowanie.

Cytat(nospor @ 9.01.2023, 16:30:43 ) *
No i co z tego ze wypelni inne pola? Ty z post w petli bierzesz tylko pola z danego typu a nie wszystkie

No dla product_properties tak jest jak mówisz. Ale zauważ, że najpierw trzeba dodac produkt, żeby znać jego ID. I to realizują linijki 6-8 z mojego kodu wyżej. A nie można wrzucić tego w pętlę do sprawdzenia czy typ istnieje w tablicy typów, no bo produkt ma się dodać tylko raz - wynika to z bazy, na której konstrukcję sam mnie naproawdziłeś TUTAJ.

nospor
Cytat
No dla product_properties tak jest jak mówisz. Ale zauważ, że najpierw trzeba dodac produkt, żeby znać jego ID. I to realizują linijki 6-8 z mojego kodu wyżej. A nie można wrzucić tego w pętlę do sprawdzenia czy typ istnieje w tablicy typów, no bo produkt ma się dodać tylko raz - wynika to z bazy, na której konstrukcję sam mnie naproawdziłeś TUTAJ.

No bo prawidlowo najpierw sie robi walidacje wszystkiego, czy wszystko jest ok itp a dopiero potem sie robi dodawanie. Ty nie masz zadnej walidacji, to jest jak jest
sadistic_son
Rozumiem, że walidacja jest niezbędna, tylko jak ją zrobić bez użycia konstrukcji warunkowych, bez ifów czy switchy? Jak bez tego mam sprawdzić czy to co user wysłał POSTem jest ok z tym co mam w bazie? Musiałbym na jakiejś zasadzie sprawdzić czy wartość z selecta (dvd, book, furniture) jest w tablicy wypełnionych inputów tekstowych, ale i tutaj nie mam pomysłu jak to zrobić bez warunków, czy to przy użyciu in_array, czy array_search itd.
Salvation
Poczytaj o klasie Validatora.
sadistic_son
Trochę jest tych klas w wynikach google. Np. TO mnie zainteresowało. Ale to zwaliduje poprawność wpisania danych jak sobie założymy, np. odpowiedni format. Nadal nie rozumiem jak uniknąć konstrukcji warunkowych do porównania inputu z bazą.
Salvation
Tutaj masz paczkę Validatora od Symfony: https://symfony.com/doc/current/components/validator.html
Jak się dobrze wczytasz w dokumentację, to trafisz w końcu na typ `Choice`: https://symfony.com/doc/current/reference/c...nts/Choice.html
A teraz poskładaj klocki w całość i działaj.
sadistic_son
Cytat(Salvation @ 10.01.2023, 09:00:49 ) *
Tutaj masz paczkę Validatora od Symfony: https://symfony.com/doc/current/components/validator.html
Jak się dobrze wczytasz w dokumentację, to trafisz w końcu na typ `Choice`: https://symfony.com/doc/current/reference/c...nts/Choice.html
A teraz poskładaj klocki w całość i działaj.

Doceniam Twoją pomoc i naprowadzenie mnie, a wręcz podanie rozwiązania niemalże na tacy. Ale niestety mam w projekcie narzucone jasno: "czyste klasy, żadnych frameworków, podejście OOP". Nie mogę użyć komponentów Symfony sad.gif
nospor
Cytat
tylko jak ją zrobić bez użycia konstrukcji warunkowych, bez ifów czy switchy?

Troche za bardzo bierzesz to co miales napisane. Bez ifow to miales znalezc wlasciwe pola z POSTa i to juz masz.
A zeby zrobic walidacje to nei da sie nie uzywac IFow i tyle. Wiec nie odlatuj za bardzo wink.gif

Najlepiej by bylo jakbys do swoich wlasciwosci w bazie dodal jeszcze info o type danych jakie tam mozna wpisac: INT, FLOAT, TEXT i na tej podstawie mozna dokladniej walidowac dane.
A jesli nie chcesz tego dodawac to sprawdzaj chociaz czy to nie jest puste

Czylu majac swoja tablice properties, lecisz foreach po properties i sprawdzasz empty($_POST[$postIndex]). Jesli ktores jest empty to walisz message i nie przechodzisz do dodawania i cala walidacja. 5 minut roboty. Mozesz jeszcze dodac walidacje na nazwe produktu itp.
sadistic_son
Cytat(nospor @ 10.01.2023, 09:21:30 ) *
Czylu majac swoja tablice properties, lecisz foreach po properties i sprawdzasz empty($_POST[$postIndex]). Jesli ktores jest empty to walisz message i nie przechodzisz do dodawania i cala walidacja. 5 minut roboty. Mozesz jeszcze dodac walidacje na nazwe produktu itp.


O, i teraz doszliśmy do zamierzonego efektu! Wspaniale!. Jedynie co jeszcze chciałbym zmienić to zastosować "don't repeat yourself". Teraz mam dwie pętle foreach które zawierają w sobie jedynie inne instrukcje. Wklejam całą tą metodę:
  1. //tak, wiem, $_POST jest zupełnie niezabezpieczony jeszcze i brakuje bindowania. Wszystko to będzie.
  2. public function insertNewProduct($sku, $name, $price, $productType)
  3. {
  4. if(empty($_POST['sku']) || empty($_POST['name']) || empty($_POST['price']) || empty($_POST['productType'])){
  5. return false;
  6. }
  7. $db = new DB();
  8.  
  9. $name = $_POST['name'];
  10. $selectedType = $_POST['productType'];
  11.  
  12. $properties = $this->groupFullProperties();
  13.  
  14. foreach ($properties[$selectedType] as $propertyData) {
  15. $postIndex = $propertyData['property'];
  16. if (empty($_POST[$postIndex]))
  17. return false;
  18. }
  19.  
  20. $queryInsertProduct = "INSERT INTO `product` (`id`, `sku`, `name`, `price`, `type`) VALUES (null, '$_POST[sku]', '$name' , '$_POST[price]', '$selectedType')";
  21. $db->query($queryInsertProduct);
  22. $db->execute();
  23.  
  24. $lastProductId = $db->lastInsertId();
  25.  
  26. foreach ($properties[$selectedType] as $propertyData) {
  27. $postIndex = $propertyData['property'];
  28. $propertyId = $propertyData['id'];
  29. $queryInsertProductProperty = "INSERT INTO `product_property` (`id`, `product_id`, `property_id`, `value`) VALUES (null, $lastProductId , $propertyId, '$_POST[$postIndex]')";
  30. $db->query($queryInsertProductProperty);
  31. $db->execute();
  32. }
  33.  
  34.  
  35. }
Wszystko działa teraz pieknie, ale widzimy powyżej, że w linijkach 14 oraz 26 jest to samo. Może wrzucić tę pętlę w oddzielną metodę i wywoływać w zależności od tego gdzie potrzebuję? Ale też nie, bo ciała pętli są różne.
nospor
Ciala tych foreach so totalnie inne. Nie ma co kombinowac

Inna sprawa to ze walidacja powinna byc w innej metodzie a nie w insert
sadistic_son
Cytat(nospor @ 10.01.2023, 10:23:07 ) *
Inna sprawa to ze walidacja powinna byc w innej metodzie a nie w insert
O, good point. Zaraz się zrobi smile.gif

Dzięki za wkład w temat!
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.