Cześć,
Zaczynam naukę OOP i w napisałem skrypt który umożliwia rejestracje/logowanie/zwrot/wypożyczenie książek.
Mam świadomość istnienia SOLID oraz PSR - czytałem o PSR 1 / 2 i staram się przestrzegać.
Nie znam MVC ani testów np. PHPUnit - to będą kolejne kroki w nauce.
Czy taki skrypt można określić obiektowym, czy jest to jakaś hybryda? Zanim pójdę dalej jw chciałbym nauczyć się dobrych nawyków i w prawidłowy sposób posługiwać się OOP.
Skrypt nie wszedł na forum, umieściłem na githubie
https://github.com/filipdak/libraryoop
Moje dodatkowe pytania:
1. Tworzenie obiektu Database w konstruktorze innych klas, czy jest to prawidłowe?
2. Metoda checkCredentials klasy log - czy nie narusza zasady pojedynczej odpowiedzialności? Czy nie powinna np. wyszukiwać użytkownika,a następnie inna metoda powinna weryfikować dane?
Dzięki za wszystkie uwagi i poświęcony czas
Najpierw zastosuj autoloader, potem wyrzuć wszystkie htmle z plików z klasami i będzie można zacząć czytać. Jaki jest sens database?
klasa Log jest dość mocno myląca, brak namespace'ów (autoloadera j/w)
metoda "insert" z form tez jest, ze tak powiem, dosc dziwna, ponieważ insert w formularzu a insert do bazy danych to dwie rozne odpowiedzialnosci.
foreach ($b as $a){
1. Dodałem autoloadera
2. Nie znam smarty ani innego template engine, więc HTML wrzuciłem w echo, jeśli to miałeś na myśli. Czy chodziło o rozdzielenie o tyle o ile to możliwe np. index i index_view, ale bez żadnego template engine?
3. Klasa Database w zamyśle miała być universalna do crud, nie wszystko jest z niej wykorzystywane w tym projekcie. Czy korzystając z PDO powinna ona tylko nawiązywać połączenie, a wszelkie operacje na bazie powinny być już napisane w innych klasach?
np. funkcje registerUser w klasie RegistrationForm wygląda następująco:
public function registerUser($data){ if($this->isError==false){ $this->connection->query("INSERT INTO uzytkownicy VALUES(NULL,:nick,:email,:password)"); $this->connection->bind(":nick",$data['nick']); $this->connection->bind(":email",$data['email']); $this->connection->bind(":password", password_hash($data['password'],PASSWORD_DEFAULT)); $this->connection->execute(); }
foreach ($b as $a){
Niestety trochę błądzisz, ale moim zdaniem to bardzo dobra decyzja, że próbujesz coś napisać samodzielnie zamiast uczyć się frameworka. Frameworki niestety uczą złych nawyków jeśli nie umiesz w porządne OOP.
To od czego bym zaczął na Twoim miejscu to composer. Composer dzisiaj to jest standard w każdym projekcie i warto go użyć już teraz choćby po to aby mieć autoloader.
Druga rzecz to PSR - pisałeś, że czytałeś i starasz się stosować, ale w kodzie tego zupełnie nie widać... Polecam zainstalować php-cs-fixer i odpalić w projekcie, a potem zobaczyć sobie jakimś diffem co zostało zmienione.
Mamy PHP 7.3, a w twoim klasach tego nie widać - nie ma typowania parametrów funkcji, typów zwracanych itp.
Z takich pierdół unikaj jak ognia stosowania if... else, a jeszcze bardziej zagnieżdżania ifów bo to zło wcielone.
Takich rzeczy jak to nie da się czytać
Tak wiem, błądzę muszę poukładać wiedze.
1. zrobiłem composera i dodałem autoloada z composera - rzeczywiście prościej niż ręcznie, podszkalam się z niego.
2. zrobiłem też php-cs-fixer - muszę tylko rozgryźć diffa bo chwilowo robię podgląd zmian ręcznie na klonie.
3. dałem type hinting
4. chyba zrozumiałem intencje z ifami i rzeczywiście wygląda lepiej, poprawiłem kod w tym zakresie jeszcze w pozostałych miejscach, nie wpadłbym na takie podejście, dzięki.
5. dependency injection wiem o co chodzi, dajemy jako argument konstruktora obiekt klasy, nie pomyślałem, żeby wykorzystać to do zrobienia połączenia z bazą danych, ale co do swojego połączenie w konstruktorze nie byłem przekonany, stad pytałem o to w pierwszym poście. ( nie tylko konstruktora oczywiście, ale generalnie jest konieczne jest dziedziczenie klas i jako argument metody w klasie która dziedziczy damy obiekt rodzica klasy )
6. co do polskiego w bazie wiem, ale obejrzałem się za późno, a nie o bazę w tym projekcie chodziło stąd rozstawiłem
7. zmieniłem komentarze na odpowiednie znaczniki, jeśli chodzi o docblock pisząc output i input masz na myśli meta tagi param i return w dokumentacji?
I w takim razie czy to byłby prawidłowy zapis?
/** * checking credentials * @param array $argument1 array structure to verify credentials * @return bool Return bool if credentials are correct */ public function checkCredentials(http://www.php.net/array $data) :bool { $this->connection->query("SELECT * FROM uzytkownicy WHERE nick=:nick"); $this->connection->bind(":nick", $data['nick']); $this->connection->execute(); if($this->connection->rowCount()==0){ $this->isError=true; $this->error="Incorect nick or password"; return false; } if(!password_verify($data["password"], $this->connection->singleResult()->haslo)){ $this->isError=true; $this->error="Incorect nick or password"; return false; } $this->isLoged=true; return true;
Przykładowo ta nieszczęsna klasa database. W przypadku błędu łapiesz wyjątek i w zasadzie nic z tym nie robisz. Czyli w sytuacji gdy wystąpił błąd mogę zrobić coś takiego:
$this->connection w query jest nullem - php sypie błędem.
(new Database())->query();
Usunąłem vendor/autoload w klasach - pozostałość bo tworzeniu połączenia w konstruktorze poprzez new Database. Później zrobiłem Depenedncy injection, a require zostało - dzięki za zwrócenie uwagi.
Co to rozszerzania Form o Database - w tutorialu OOP który przerabiałem Dependency injection było pokazywane tylko na klasach które po sobie dziedziczą - uznałem to za obowiązkowe, a rzeczywiście nie jest i już usunięte.
Rozpisałem checkCredentials na przyjmowanie 2 stringów - w arrayu i tak siedział tam tylko user i password.
Dzięki za info o smartach, przyjrzę się pozostałym.
Możesz przybliżyć o co chodzi z userservice? Kompletnie nie wiem jak mogę to ugryźć.Chodzi o stworzenie nowej klasy userService i tam przeniesienie checkcredential? Tylko, że to główne zadanie klasy loginForm.
Zapytam przy okazji czy to jest prawidłowe, lepsze podejście to strony index w przypadku logowania? Uzależnienie wyświetlanej treści od tego czy jesteśmy zalogowani czy nie, czy lepsze jest przekierowywanie jak wykryje, ze jesteśmy zalogowani?
<?php $user = new User(); if ($user->isLoggedIn()) { ?> <p>Hello <a href="profile.php?user=<?php http://www.php.net/echo escape($user->data()->username); ?>"><?php http://www.php.net/echo escape($user->data()->username); ?></a>!</p> <ul> <li><a href="update.php">Update Details</a></li> <li><a href="changepassword.php">Change Password</a></li> <li><a href="logout.php">Logout</a></li> </ul> <?php } else { http://www.php.net/echo "<p>You need to <a href='login.php'>login</a> or <a href='register.php'>register</a></p>"; } ?>
Ogólnie po zmianach wygląda już znacznie lepiej. Postaram się dać trochę uwag - to jeszcze nie wszystko co rzuciło mi się w oczy, ale nie chcę Cię wszystkim przytłaczać na raz.
1. GIT - w gicie masz coś takiego jak .gitignore pozwalające na nie commitowanie pewnych plików. Dla przykładu w 99% projektów nie komituje się vendorów, a już na pewno nie powinieneś commitować cachu od fixera, czy innych cachy/logów. Tu przy okazji będziesz miał fajne ćwiczenie jak przestać śledzić zmiany w repo czegoś co omyłkowo commitowałeś. No i zdecydowanie popracuj nad opisywaniem commitów - opis powinien być jasny i klarowny, a nie @Update. Jak będziesz kiedyś robił repo, które chcesz pokazać pracodawcy, to historia commitów jest często przeglądana. W firmach zazwyczaj pracuje się w systemach zadaniowych i opis kommita wygląda tak: [Task-abc] Opis taska.
2. PHP coś z tym fixerem poszło nie do końca ok, bo w wielu miejscach nadal masz kod niezgodny z PSR - np. $a=1 powinno być $a = 1; Ogólnie sporo masz tam jeszcze w kwestii PSR do poprawienia. Ale znów - jeśli myślisz o pracy zawodowo, to jest coś na co pracodawcy baaaardzo zwracają uwagę, więc wyrabiaj sobie nawyki.
3. Piszesz, że walczysz z diffem - są do tego narzędzia, które pokazują graficznie. Ja korzystam akurat z wbudowanego w PhpStorma, ale to płatne oprogramowanie, więc pewnie musisz poszukać jakiś alternatyw - może vscode ma podobne wtyczki, albo jakiś wyspecjalizowany edytor. W PphSotrm jest nawet opcja wpięcia fixera jako plugin, który sam poprawia PSR w pliku. Umiejętność pracy z Diffem jest kluczowa w pracy zespołowej, gdzie musisz sprawdzić co kto i kiedy zmieniał. Warto nad tym pracować.
4. Komentarze. Ogólnie tutaj szkoły są różne. Ja jestem zwolennikiem, że kod powinien się dokumentować sam, gdzie tylko to możliwe. Ogólnie komentarze powinny być stosowane gdy wnoszą coś nowego - jeśli są redundantne z metodą to są zbędne. Spójrz tutaj:
https://martinfowler.com/eaaCatalog/
Dlaczego głównym zadaniem loginForm ma być sprawdzanie stanu użytkownika? Klasa powinna zawierać walidację, filtrowanie danych, ewentualnie zajmować się generowaniem inputów (na zasadzie buildera $loginForm->add('input', [params]).
Njapierw klasa dziedziczy po Database
class RegistrationForm extends Database
A potem ta sama klasa w konstruktorze dostaje obiekt Database
public function __construct(Database $connection)
Rozumiem, ze zapomniales usunac extednds Database ?
Klasa Database
require_once('config\config.php'); class Database { private $host=DB_HOST; private $user=DB_USER; private $password=DB_PASSWORD; private $dbname=DB_NAM
try { $this->connection= new PDO($dbh, $this->user, $this->password, $options); $this->isConnected =true; } catch (PDOException $e) { $this->error=$e->getmessage(); $this->isConnected = false; }
<?php http://www.php.net/session_start(); if (!http://www.php.net/isset($_SESSION['loggedin'])) { http://www.php.net/header("location:index.php"); } http://www.php.net/echo <<<END <a href="borrow.php"> Borrow book </a></br> <a href="return.php"> Return book </a></br> <a href="history.php"> Account history </a></br> <a href="logout.php"> Logout </a></br> END;
<?php http://www.php.net/session_start(); if (!http://www.php.net/isset($_SESSION['loggedin'])) { http://www.php.net/header("location:index.php"); } ?> <a href="borrow.php"> Borrow book </a></br> <a href="return.php"> Return book </a></br> <a href="history.php"> Account history </a></br> <a href="logout.php"> Logout </a>
Jeszcze taka ogólna uwaga odnośnie dziedziczenia. Ogólnie dziedziczenie nie służy do "przekazywania" kodu do klas potomnych tak jak to robisz, czyli rozszerzając klasę o klasę Database. Dziedziczenie zazwyczaj to zło, chyba że robisz to świadomi i w przemyślany sposób. W większości przypadków lepsza od dziedziczenia jest kompozycja (wstrzyknięcie obiektu przez konstruktor). Jest to bardziej elastyczne rozwiązanie i nie blokuje rozwoju aplikacji. Dziedziczenie w prawdziwych aplikacjach używane jest dość rzadko - raczej stosuje się interfejsy + kompozycję. Na tak małym kodzie jak teraz piszesz może to być trudne do zrozumienia i wydawać się całkiem dobrym pomysłem, ale serio używaj dziedziczenia jak najrzadziej się da, bo przy większym kodzie to się szybko mści.
Ustosunkuje się tylko niektórych elementów, bo dostałem dużo wskazówek, a zanim je przerobie minie dłuższa chwila.
1) Tak, nie wrzuciłem na githuba dokonanych w plikach zmian stąd w dalszym ciągu było widoczne extends - jak pisałem już tak było wskazane w tutorialu który przerobiłem dotyczący dependency injection, że można było je stosować tylko w przypadku dziedziczenia.Odniosłem po tym tutorialu wrażenie, że dziedziczenie jest bardzo ważne i szeroko stosowane, ale jak widać nie
https://github.com/filipdak/libraryoop
2) Jeśli chodzi o
$log->getIsLoged()
To o czym piszesz to jest częsty błąd w tutorialach, bo prawdziwe OOP trudno ująć w krótkim tutorialu. Szybciej się go nauczysz pisząc jakąś apkę wspólnie z kimś kto temat ogarnia. Największe oszustwo OOP polega na tym, że ludzie próbują przenosić taksonomie z prawdziwego życia. Na przykład typowy przykład - mam psa i rybkę -> stworzę klasę Animal. To jest w 80% tutoriali o OOP, a jest to anty przykład jak stosować OOP. Tak jak pisałem dzisiaj będzie Ci to trudno zrozumieć bo piszesz zbyt mały kod, ale w prawdziwym kodzie to jest droga do wielkiej katastrofy.
Wpisz w Google jedną z podstawowych zasad OOP "Favor composition over inheritance" - na razie nie zaprzątaj sobie tym głowy bo w Twoim kodzie są większe problemy niż stosowanie SOLID, ale ogólnie dziedziczenia staraj się unikać to zaprocentuje w przyszłości.
* tu taki disclaimer - ja nie jetem przeciwnikiem dziedziczenia. W wielu miejscach jest bardzo przydatne - np. metoda szablonowa etc. Ale na pewno nie w takiej formie jak uczą go w tutorialach dla początkujących. To tylko tworzenie złych nawyków, które potem trudno zwalczyć, a kompozycja jest bardzo łatwym konceptem do ogarnięcia więc można od razu wyrabiać dobre wzorce.
Tymczasem do działa - czekam na kolejną wersję kodu. Jeszcze z 20 -30 iteracji i będzie dobrze ;-)
PS. z tym HTML to chyba źle zrozumiałeś intencje kolegi - chodziło mu o zupełne wydzielenie widoku z logiki, a nie zamianę kodu html na stringi w PHP. Jak poczytasz o MVC to zobaczysz o co chodzi. Nie musisz tu używać TWIGA/SMARTY - widok możesz zrobić na prostym PHP + HTML, ale nie mieszaj widoku z logiką.
$log->getIsLoged()
Zakręciłem się i pracowałem na klonie, a zrobiłem upload nie klona z jakimiś innymi drobnymi poprawkami. Już na pewno nie ma extends.
Nie byłem pewny czy chodzi o wyechowanie htmla, czy właśnie jak pytałem o zrobienie np. index do którego includuje index_view, który to zawiera html - ale miałem problem żeby oddzielić to w 100%.
Korzystając z okazji zanim będę przerabiał temat to dopytam właśnie o to.
Oddzielenie html od php na przykładzie z dużym echem - chodzi mi o sam sposób, a nie czy w tym pliku to rzeczywiście było potrzebne
mysite
<?php http://www.php.net/session_start(); if (!http://www.php.net/isset($_SESSION['loggedin'])) { http://www.php.net/header("location:index.php"); } require("mysite_view.php");
<a href="borrow.php"> Borrow book </a></br> <a href="return.php"> Return book </a></br> <a href="history.php"> Account history </a></br> <a href="logout.php"> Logout </a></br>
<?php http://www.php.net/session_start(); if (!http://www.php.net/isset($_SESSION['loggedin'])) { http://www.php.net/header("location:index.php"); } require __DIR__ . '/vendor/autoload.php'; use \Library\Library; use \Library\Database; $db = new Database(); $book = new Library($db); if (http://www.php.net/isset($_POST['return'])) { $book->returnBook($_POST); } http://www.php.net/echo '<form name="return" method="post">'; foreach ($book->getBorrowedBooks() as $position) { http://www.php.net/echo $position->autor.$position->tytul.$position->rok."<input type='checkbox' name='return[]' value=".$position->idksiazki."></br>"; } http://www.php.net/echo '<input type="submit" value="Return"></form><a href="mysite.php">Back</a>';
Generalnie nie chodzi o odzielenie php od html a o odzielenie logiki aplikacji od wyswietlania. W logice aplikacji przygotowujesz wszystkie dane, ktore potem przekazujesz do widoku ktory to te dane wysweitla.
A czy widoko zrealizujesz w php czy za pomoca systemu szablonow to juz inna sprawa.
Ok czyli MVC się kłania. Dzięki za wyjaśnienia, " I'll be back "
Wiele już zostało powiedziane ale ja dodam od siebie jedno, na co nikt wcześniej nie zwrócił uwagi. Chodzi o:
https://github.com/filipdak/libraryoop/blob/master/class/RegistrationForm.php#L103
$this->connection->query("INSERT INTO uzytkownicy VALUES(NULL,:nick,:email,:password)");
$this->connection->query("INSERT INTO uzytkownicy (nick, email, password) VALUES(:nick,:email,:password)");
Trochę mnie nie było, jednak musiałem przyswoić dość dużą dawkę wiedzy.
Napisałem praktycznie wszystko od nowa.
1) Oparte jest teraz na MVC - tak, wiem prymitywne MVC - przerabiałem też MVC oparte na wyrażeniach regularnych i preg_match, co w przyszłości tutaj wdrożę jednak w celach naukowych takie prymitywne lepiej mi wszystko ilustrowało.
2) Dodane template engine Twig
3) Usunięte zapytania SQL bazujące na kolejności kolumn w bazie
4) Dodałem obsługę wyjątków (?) czy jest to zrobione poprawnie? Wcześniej była mowa, że wyjątki są duszone w środku
5) Było mówione o dziedziczeniu klas - tutaj wszystkie Controllers dziedziczą po głównym kontrolerze Core\Controller - czy jest to akceptowalne?
6)Wiem, że muszę po upraszczać trochę kod bo miejscami if na ifie.
7)Mam problem z Type Hinting w Model User metoda authenticate - zwracam obiekt lub bool, a z tego co czytałem dopiero od php 8 będzie możliwość typowania dwóch różnych form.
Wszystko wgrałem pod nowy link, ponieważ mało rzeczy jest wspólnych z poprzednim.
Jak zwykle dzięki.
http://github.com/filipdak/librarymvc
Jest zdecydowanie lepiej.
echo View::renderTemplate('mysite/borrow.html', ['books' => Library::getBooks()]);
echo raczej jest tu zbedne
Jestes bardzo nie konsekwenty i tworzysz cala mase klas, ktore w polowie sobie obiektami a w polowie maja metody statyczne. Ok, czasami metoda statyczna moze i sie przydac, ale tutaj u ciebie w wiekszosci wypadkow jest to zle zaprojekotwane
Uzywaj === oraz !==
Okreslaj wszedzie typy a nie tylko w polowie plikow
Zainteresuj sie narzedziami do statycznej analizy kodu, np phpstan. Odwala za ciebei polowe roboty.
if ($code = '500') {
Ty w ogole testujesz kod, ktory piszesz? Juz nie mowie nawet o phpunit ale o zwyklym manualnym tescie...
Staraj sie optymalizowac swoj kod, np
private function getNamespace(): string
{
$namespace = "App\Controllers\\";
return $namespace;
}
mozna poprostu napisac
private function getNamespace(): string
{
return "App\Controllers\\";
}
Nie ma sensu tworzyc zbednych zmiennych niczego nie wnoszacych
class Config { const DB_HOST = 'localhost'; const DB_NAME = 'librarymvc'; const DB_USER = 'root'; const DB_PASSWORD = ''; }
No prosze a już myślałem, że się poddałeś ;-)
Kod wygląda o niebo lepiej niż pierwsza wersja - wreszcie zaczyna przypominać jakąś strukturę używaną w 21 wieku, więc widać że czasu nie zmarnowałeś. Ale nie będę Ci tu słodził bo nie po to tu piszesz.
Moje uwagi
- brak konfiguracji o czym pisał nospor - rzeczy, które mogą się zmieniać powinny być wydzielone do osobnego miejsca. Np. plik yaml + obiektowy wraper na to pozwalający odczytywać dane konfiguracyje
- jak używasz static to na 99% możesz być pewien, że popełniasz błąd projektowy. Static to taki nowy stan globalny i z aplikacji powinien zniknąć, bo w ten sposób ukrywasz niepotrzebnie zależności. Wszystkie zależności powinny być wstrzykiwane przez konstruktor choćby dlatego, że wtedy widzisz, ze robi się ich w pewnym momencie za dużo i jest pora na refactoring. Tak więc moja rada - usuń wszystkie odwołania static.
- brakuje mi w Twoim kodzie obiektów Request / Response. Echo powinno pojawić się w kodzie aplikacji tylko raz, gdy renderujesz Response. U ciebie tekst jest printowany w kontrolerach, autoryzacji itp. Masz różne dziwne kontrukcje typu przekierowanie headerem w kontrolerze itp... Tak nie powinno być - powinieneś mieć obiekt Response - najlepiej w kilku odmianach typu "NotAuthorizedResposne / NotFoundResponse / RedirectResponse" itp. Contoller powinien zwracać taki Response i front controller albo powinien decydować co dalej z tym zrobić. W ten sposób bedziesz miał piękne polimorficzne kontrollery, które zawsze będą zachowywać się tak samo, czyli zwracać obiekt ResponseInterface lub AbstractResponse. Ewentualnie zamiast zwracać niektóre Respony można rzucać wyjątki typu NotFoundException i przechwytywać je we frontkontolerze (gdyby go miał).
- Brakuje mi obiektu FrontController - > to ten obiekt powinien inicjować Request, Router i obsłgiwać Response zwrócony z kontrolera
- Pytałeś o dziedziczenie po kontrolerze - tutaj jak najbardziej moim zdaniem ma to sens, bo kontroler ma dużo kodu, który jest wszędzie potrzebny, więc dziedziczenie jest uzasadnione.Warto byłoby aby była to klasa abstrakcyjna.
- kod ciągle nie jest zgodny z PSR
Ogólnie błędów jest sporo więcej, ale jak poprawisz to + to co napisał nospor możemy iść dalej ;-)
Gratki za postępy.
Dzięki za uwagi:)
1) Powalczę z gitem - zamiast nie śledzić więcej vendor, przypadkowo usunąłem. Przerobie tutorial i to naprawie.
2) Odnośnie Config - Propel/Doctrine to coś czym powinienem się zainteresować czy zbłądziłem? Temat jest mi zupełnie obcy.
3) Co do static poczytałem i wiem o co już chodzi - przerobie na Dependency injection.
4) PHPStan zainstalowałem, znalazł kilka niespójności - dzięki za informacje o czymś takim.
5) Co do front controllera jestem trochę zmieszany, zgłębiam temat i coś świta, jednak nie znalazłem żadnych dokończonych wątków na ten temat:)
Przeglądałem sporo poradników, jednak większość podstawowych MVC było skróconych tylko do rutera. Z tego co zebrałem w całość:
Czy chodzi o takie przebieg wydarzeń? Front controller przekazuje do Routera request, ten je rozbraja i zwraca controller,action, params. Następnie frontcontroller pełni też rolę dispatchera i wywołuje odpowiednie kontrolery. Nie generują one renderTemplate tylko generują response, która trafia do front controller i on dopiero odpala renderTemplate etc? Jeśli tak, brzmi abstrakcyjnie. Tutoriale o klasach animal nie wiele mają z tym wspólnego
Front Controller -> Request ->Dispatcher -> Controler -> Response ->front Controller || /\ \/ || router
Tak w telegraficzny skrócie, to to co zaproponowałeś wygląda jak w miarę sensowny przebieg.
Front controller odpala Router, ten z kolei na podstawie danych z Request dispatchuje przebieg do odpowiedniego kontrolera.
Kontroler zwraca Response. W takiej minimalistycznej wersji Response powinno zawierać:
- body (do body renderujesz np szablon w kontrolerze)
- headery (np. content-type)
- status code (np. 200/404 itd)
Front controller po otrzymaniu responsa "wysyła" go do przeglądarki (lub innego klienta) - zazwyczaj coś w stylu $response->send() - metoda send zajmuje się wyświetleniem / ustawieniem hederów itp. Czyli np. jak response będzie 301 to $response->send() wykona przekierowanie, a jak 200 to wyświetli zawartość w zależności od content-type (bo przecież to może być html ale też np. json lub xml).
Obiektów response zazwyczaj jest kilka i mają one wspólny interfejs - w ten sposób jak za miesiąc Twoja apka będzie musiał w jakiejś akcji zwrócić json zamiast html to po prostu utworzysz sobie JsonResponse implementujący interfejs Response i nic nie będziesz musiał zmieniać w szkielecie.
To jest własnie kluczowa sprawa w OOP żeby tworzyć strukturę, która do rozszerzenia nie wymaga edycji już istniejącego kodu. Jeśli twój front Kontroller będzie akceptował generyczny Response to mu wszystko co się dzieje w $response->send().
Oczywiście to tylko jedno z możliwych podejść.
Możesz też zmienić podejście i poczytać o https://www.php-fig.org/psr/psr-15/
Utknąłem, nie mam pomysłu, przeglądałem dokumentacje Symphony i Zenda, ciężko znaleźć coś konkretnego o front controlerze - w dokumentacji tych frameworków fc nie jest obiektem, a jeśli dobrze Was zrozumiłem fc miał być obiektem.
Ponadto utknąłem na etapie gdy kontroler wywołuje return new Response - nie mam pomysłu jak front controller ma przechwycić return new Response, skoro to dispath w ruterze wywołuje tą metodę w linijce
Kojarzycie jakieś artykuły z rozpisanym MVC w taki sposób, że fc obsługuje response?
$controller_object->$action();
Najprościej chyba możesz to zrobić przechwytując we front controlerze rezultat zwrócony przez dispatcher np. tak:
Zrobiłem tak, że kontroler zwraca Response, a w Ruterze w dispatch dodałem przy wywołaniu również return:
return $controller_object->$action();
$response = $this->router->dispatch($request);
Widze nadal kodu nie testujesz
FrontController.php
if ($response->getStatusCode() == 301) {
header('location:$file.php');
}
Po pierwsze zmienne w pojedynczych ciapkach nie sa parsowane, po drugie zmienna $file w ogole tam nie istnieje
Naucz sie uzywac === zamiast ==. Zaoszczedzi ci to kiedys sporo czasu na szukaniiu glupich bledow
return Yaml::parseFile('C:\xampp\htdocs\LibraryResponse\Core\db.yaml');
Gdy prosilismy bys wywalil konfig z gita, naprawde nie chodzilo nam bys go zastapil sciezka do pliku, do ktorego nie mamy dostepu
public static function getDB(): object
Czemu to zwraca object? Przeciez wiesz, ze to zwraca PDO a nie byle jaki objekt
Taki kod
$params = http://www.php.net/array(); http://www.php.net/array_merge($params, $getParams); http://www.php.net/array_merge($params, $postParams); $this->params = $params;
$this->params =http://www.php.net/array_merge($postParams, $getParams)
if (is_callable([$controller_object, $action])) { return $controller_object->$action(); } else { throw new Exception("Method $action (in controller $controller) not found and not returned response"); }
if (is_callable([$controller_object, $action])) { return $controller_object->$action(); } throw new Exception("Method $action (in controller $controller) not found and not returned response");
Przez ostatni czas przerabiałem Laravela, teraz wróciłem do czystego PHP. Przez 2 miesiące nauki frameworka wiele rzeczy o których pisaliście stało się jasnych, więc wracam z serią pytań. Uporządkowałem gitHuba wyrzucając wszystko co stare, ponieważ i tak był tam syf, napisałem MVC od nowa.
1. Wspominaliście o obiektowym wraperze YAMLa, którego nie mogę wykminić, czy taki sposób jest ok? Wspominaliście
Plik Core/Model
public function getConfig() { return Yaml::parseFile("../Config/db.yaml"); }
public function run(): void { try { $request = Request::createFromGlobals(); $response = $this->router->dispatch($request); $response->send(); } catch (AccessForbiddenException $e) { $this->handleError($e, 403); } catch (PageNotFoundException $e) { $this->handleError($e, 404); } catch (\Exception $e) { $this->handleError($e, 500); } }
Zrozum, rzeczy konfiguracyjne nie moga lezej w GIT. Kazdy moze miec inna konfiguracje do bazy. Wiec jak ja zmienie swoja konfiguracje do bazy to co? Mam komitowac? Ale wtedy tobie napsuje bo ty masz inna, no nie?
PLiki konfiguracyjne maja byc w .gitignore i maja nie byc zapisywane w git
W git co najwyzej mozesz zapisac sobie plik db.yml.dist ktory bedzie sluzyl jako wzor na stworzenie swojego wlasnego lokalnego pliku konfiguracyjnego. Ale to wszystko jest w znanych w FW i w laravel, ktore rzekomo uzywales
Dzięki teraz wiem o co chodzi. Tak używam laravela ale nie commitowalem go na github, jest mi zupełnie obca praca kilku osób nad projektem i stąd takie historie.
Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)