Drukowana wersja tematu

Kliknij tu, aby zobaczyć temat w orginalnym formacie

Forum PHP.pl _ Oceny _ DbM Framework - Autorska aplikacja frameworka opartego na wzorcu MVC

Napisany przez: Malinaa 20.12.2023, 21:40:29

Witajcie,
miałem chwilę i usiadłem do autorskiego frameworka opartego na wzorcu MVC.
Chciałbym go dopracować, utworzyć wersje stabilną.

Kod frameworka jest dostępny pod adresem:
https://github.com/artimman/dbmframework

Zakończyłem pierwszy test aplikacji na serwerze zdalnym - powodzeniem.
Wydaje się, że już jest całkiem Ok i ciekawe, czy ktoś się ze mną zgodzi?

Napisany przez: nospor 21.12.2023, 09:42:45

ok, to na plus:
+ znajomosc jako tako klas
+ uzywanie typowania w wiekszosci miejsc.
+ prawie uzywanie najnowszej wersji php wink.gif


a teraz troche krytki:
w ogole na poczatek to zaprzyjaznij sie z takmi narzedziami jak :
- php-cs-fixer
- phpstan
- inne do statycznej analizy kodu
One wylapia ci na dzien dobry naprawde duzo bledow
I generalnie mowie tutaj o bledach ktore nie sa moze jakos krytyczne, ale w dluzszej perspektywie beda uciazliwe do poprawnej pracy z twoim kodem.


NIe wiem jak planujesz docelowo dostarczac swoj FW, ale na chwile obecna widze wrzuciles go fo VENDOR a ten katalog jest zastrzezony dla COMPOSER. wywal go wiec stamtad i wsadz nie wiem, np do LIBS.

No i jesli twoj kod uzywa np phpmailer, ktory jest zapisany w composer.json to nie zapisuj go w GIT. Katalog VENDOR generalnie w GIT ma sie nie znalezc w ogole. po to jest composer

plik config.php rowniez powinien nazywac sie np config.php.dist i dopiero ludzie kopiuja go sobie jako config.php lokalnie. Dlaczego? bo z kazdym updatem z twojego gita, ludzie straca swoje zmiany gdy to zostanie jak teraz

declare(strict_types=1); ma byc w kazdym pliku php a nie w co drugim

Potworki w stylu
IF
IF
IF
IF
IF
az oczy bola wink.gif NIe bede omawial wszystkich bo w pyte tego masz, ale przyklad jak to sie poprawia
Jesli masz kod
IF (costam) {
//blabla
return 'cos tam'
}

return null;

To zeby uniknac zagniezdzenia duzego to sie robi poprostu negacje na pocatku, wali nullem na poczatku a reszta duzego kodu leci juz bez zagniezdzenia czyli:
IF (!costam) {
return null;
}
//blabla
return 'cos tam'

Jak widzisz w zagniezdzeniu jest tylko return null a nie milion linijek

public function __construct()
{
try {
$this->connect = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_DATABASE, DB_USER, DB_PASSWORD, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));
$this->connect->exec("SET NAMES utf8");

return $this->connect;
} catch (PDOException $exception) {
throw new DbmException($exception->getMessage(), $exception->getCode());
}
}
No przeciez konstruktow nigdy nic nie zwraca wiec po grzyba tam return? konstruktor sam w sobie jest "returnem" wink.gif

public function querySql(string $sql, ?string $fetch = null): PDOStatement
{
if ($fetch == 'assoc') {
$stmt = $this->connect->query($sql, PDO::FETCH_ASSOC);
} else {
$stmt = $this->connect->query($sql);
}

if (!$stmt) {
throw new DbmException($this->connect->errorInfo()[2], $this->connect->errorInfo()[1]);
} else {
return $stmt;
}
}

po co tu ten fetch jest raz ze stringiem a na dodatek nullem? skoro on odpowiada tylko za dwie mozliwosci, to zrob z niego boola i juz. Dodatkowo ten drugi ELSE na dole jest totalnei zbedny. :

public function querySql(string $sql, bool $fetch = false): PDOStatement
{
if ($fetch) {
$stmt = $this->connect->query($sql, PDO::FETCH_ASSOC);
} else {
$stmt = $this->connect->query($sql);
}

if (!$stmt) {
throw new DbmException($this->connect->errorInfo()[2], $this->connect->errorInfo()[1]);
}
return $stmt;

}
Analogicznie cala masa innych funkcji tam

public function requestData(string $fieldName)
{
if ($_SERVER['REQUEST_METHOD'] == "POST" || $_SERVER['REQUEST_METHOD'] == 'post') {
if (array_key_exists($fieldName, $_POST)) {
return trim($_POST[$fieldName]);
}
} elseif ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'get') {
if (array_key_exists($fieldName, $_GET)) {
return trim($_GET[$fieldName]);
}
}
}
To ze ktos wyslal dane POSTem, nie znaczy ze dane nie znajduja sie tez w GET. wlasnie ta funkcja zlikwidowales polowe funkcjonalnosci dla ludzia.

public function setDataToDB($value)
{
$value = strip_tags($value);
$value = htmlspecialchars($value);

return $value;
}
A co ty mi tutaj kasujesz tagi z pola co chce wlozyc do bazy? Jak bede chcial skasowac to sam sobie skasuje. poza tym uzywasz bindowania wiec po co htmlspecialchars? To sie uzywa podczas wyswietlania a nie przed wkladaniem do bazy. Ta cala funkcja jest totalnie zbedna

public function userPermissions(int $user): string
{
$database = new DbmDatabase();

$query = "SELECT roles FROM dbm_user WHERE id = ?";

if ($database->queryExecute($query, [$user])) {
if ($database->rowCount() > 0) {
$data = $database->fetchObject();

return $data->roles;
} else {
return 'dataNotFound';
}
} else {
return 'dataQueryError';
}
}
Jesli funkcja zwraca role, to ma zwracac role a nie komunikaty bledow. Od bledow masz wyjatki.I znowu wpyta zagniezdzen tutaj


public static function temp_htmlUser($sessionUserId, $module = null): void
{
$database = new DatabaseClass();
$userId = (int) $sessionUserId;

$query = "SELECT user.login, user.avatar, user_details.fullname FROM dbm_user user"
. " INNER JOIN dbm_user_details user_details ON user_details.user_id = user.id"
. " WHERE user.id = '$userId'";

if ($database->queryExecute($query)) {
$data = $database->fetchObject();
Czemu ta funkcja radosnie tworzy polaczenie do bazy i jeszcze sama z siebie pobiera usera? polaczenie z baza ma byc stworzone rac w calej aplikacji i przekazywane potem do odpowiednich klas. W tym momecnie wczasie jednego request ty generujesz mase polaczen do bazy.
Rowniez obiekt usera ma byc pobrany raz i przekazywane do odpowiednich klas.

Klasa TranslationClass. Raz ze te Class w nazwie jest zbedne, a dwa raz funkcje z malej raz z z duzej... generalnie maja byc z malej.

W klasie DatabaseClass, znowy Class zbedne ale:
public function __construct()
{
try {
$this->connect = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_DATABASE, DB_USER, DB_PASSWORD, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));
$this->connect->exec("SET NAMES utf8");

Ty tutaj znowu tworzyc nowy obiekt PDO, czyli nowe polaczenia. Kazdy model z tego dziedziczy, wiec jak odpalisz 3 modele to masz juz 3 nowe polaczenia do bazy. No tak sie nie robil .Jak pisalem wczesniej, jedno polaczenia do bazy masz tworzyc i ono ma byc przekazywan tam gdzie trzeba

public function getSection(int $id): object
{
$query = "SELECT * FROM dbm_article_sections WHERE id = '$id'";

$this->queryExecute($query);

if ($this->rowCount() > 0) {
return $this->fetchObject();
}

return (object) [];
}
I znowy strasznie mieszasz style. Raz jak nie ma rekordu to zwracasz NULL a tu radosnie zwracasz pusty obiekt. No sie zdecyduj na jedno i sie tego trzymaj


public function userSigninCorrect(array $params, string $password): ?string
{
$query = "SELECT * FROM dbm_user WHERE (login=:login OR email=:email) AND verified=true LIMIT 1";

if ($this->queryExecute($query, $params)) {
if ($this->rowCount() > 0) {
$result = $this->fetchObject();

if (password_verify($password, $result->password)) {
return $result->id;
} else {
return self::VALID_PASSWORD;
}
} else {
return self::VALID_LOGIN;
}
} else {
return null;
}
}
I tutaj znowu, funkcja ktora powinna zwracac info czy user sie zalogowal czy nie to zwraca albo jako text id usera albo komunikaty bledow... No prosze cie wink.gif

public const VALID_LOGIN = 'loginNotFound';
public const VALID_PASSWORD = 'passwordNotMatched';
Chyba sie to powinno nazywac INVALID a nie VALID


Jeszcze tego tam jest torche ale juz mi sie nie chce sprawdzac. Generalnie idziesz w dobrym kierunku powiedzmy, ale musisz troche popracowac jeszcze

Napisany przez: Malinaa 21.12.2023, 11:36:53

Dzięki bardzo za sprawdzenie kodu.

Narzędzia były tu zaprzyjaźnione:
php-cs-fixer - przeleciał kod bez błędu

jako że fixer naprawdę nie pokazał żadnego błędu to phpstan sobie odpuściłem
nie wiem co w tym uciążliwego, chyba że mowa tu o doskonałym kodzie, ale o takim nie słyszałem,
co najwyżej jest idealny, gdzie po jakiejś chwili pojawia się kolejna wersja jakże doskonałego kodu.

Z katalogiem vendor ta mam taką rozkminkę co, jak? Z tego co widzę Symfony jest w /vendor, więc dlaczego chcesz wsadzić tego frameworka do /libs? Jak idealnie poukładać te katalogi?
Tutaj pojawia się więcej znaków zapytania, co kiedy zaktualizuje silnik aplikacji, przydałby się jakiś mechanizm dla aktualizacji, tylko jak go zrobići tak, aby świetnie współpracował z Composer itd.?
Katalogu vendor generalnie nie powinno być w ogóle na Gicie. W takim przypadku jak załadować silnik frameworka najlepiej do katalogu vendor? Podobnie jak w Symfony.

Plik config.php na config.php.dist - faktycznie, racja, jakoś jeszcze nie pomyślałem o tym (nawet jeszcze nie testowałem, czy Composer działa na zdalnym, skopiowałem projekt z Gita i wykonałem pierwsze uruchomienie)

declare(strict_types=1); - no to muszę jeszcze poprzeglądać plik po pliku

Milion linijek if'ów już nie dawało mi dostać się do snu w nocy - zajrzę na to, może teraz coś mi się lepszego przyśni

Z $this->connect i dalej $database->queryExecute($query) tu coś się zamotałem, kur.de jak poprawić te połączenia? Ma być jedno połączenie i pozostałe... mają z takiego korzystać.

Aby przejść dalej to muszę poprawić powyższe... na tym połączeniu z bazą danych itp. zamotałem się! Jak to poprawić?

P.S. Czy musisz rozpier.dzielać mi serducho z tym radośnie zaś?
Czuje się jak Wesoły Romek, może to lepiej niż jakiś Zenek, albo Ziutek, ale generalnie to jeden czort,
może by tak chociaż od Święta zamiast Jasiu pomyśleć Jan Kowalski, chyba widać tu różnicę smile.gif

Napisany przez: nospor 21.12.2023, 11:52:58

phpstan sprawdza innego rodzaju rzeczy wiec polecam mimo wszystko

Cytat
nie wiem co w tym uciążliwego, chyba że mowa tu o doskonałym kodzie, ale o takim nie słyszałem,
co najwyżej jest idealny, gdzie po jakiejś chwili pojawia się kolejna wersja jakże doskonałego kodu.

Jesli wywoluje twoja metode ktora powinny mi zwrocic string, a ona jako string zwraca mi jeszcze bledy i ja musze znac kod tych bledow i je obslugiwac, to jest to super uciazliwe.
Jesli odpale x modeli i one wszystkie tworza mi nowe polaczenie i mam wielu klientow na raz, to nagle baza mi zacznie mulic. znowu uciazliwe.
Jesli bede chcial z toba wspolpracowac i musial poprawiac cos w twoim kodzie, to sie zachlaszcze po dwoch godzinach wink.gif
Ja sam nie pisze idealnego kodu, zawsze jest cos do poprawki. Ale jak ktos pokazuje sensowne bledy to nie lece po ambicji.

Jak juz mowilem: VENDOR jest zarezerwowany dla COMPOSER. Twoj lib nie idzie przez composer, ty go wsadzasz na sile w GIT do VENDOR i to jest zle. Tak, Symfony jest w vendor, ale on tam wpada z composer.
Ja VENDOR moge skasowac, potem odpalic composer install i mi sie wszystko odbuduje samo. W twoim wypadku, ja skasuje VENDOR i szlag trafil projekt.
Jak mowilem, VENDOR ma nie byc w GIT. VENDOR sam sie buduje jak odpalisz composer install. Na tym to wlasnie polega a ty tego nie rozumiesz.

Co do polaczen to poczytaj o DI (dependency injection). Ewentualnie zastosuj SINGLETON - generalnie sie tego nie poleca, ale w twoim wypadku poprawi funkcjonalnosc


Cytat
P.S. Czy musisz rozpier.dzielać mi serducho z tym radośnie zaś?

Sluchaj, ja ci tego nie pisze ze zlosci, z mysla "o, kolejny koles ktoremu dopieke"...
Poswiecilem troche czasu by przejrzec twoj kod i ci pomoc przez konstruktywna krytyke.
Ale jesli zle to odbierasz to skasuj temat, zapomnij o sprawie i pisz dalej po swojemu. Tylko nie pisz wiecej ze napisales juz zajebisty kod i kto sie z toba zgadza wink.gif

Napisany przez: Malinaa 21.12.2023, 12:51:47

Kod
Na tym to wlasnie polega a ty tego nie rozumiesz.

Oj, rozumiem, co nie oznacza że już wiem jak prawie idealnie zakodować.

Trochę obracasz kota ogonem. Napisałem: "Wydaje się, że już jest całkiem Ok", czy znaczy to samo jak piszesz: "ze napisales juz zajebisty kod". Nie, ale rozumiem.
Pewnie po prostu tak masz. Wierzę, że nie piszesz po złości, gdyby było inaczej nie prosiłbym o pomoc w napisaniu bezbłędnego kodu.
Z lekka irytuje mnie radośnie jak Wesoły Romek, ale nikt nie musi o tym wiedzieć, pewnie moja zasługa/wina, że jest tak radośnie i coś dziwnie odbieram.

Dzisiaj to już nie za bardzo jest czas, przed świętami raczej nie usiądę do poprawiania.

Wesołych Świąt.
Dziękuje za pomoc.

Napisany przez: nospor 21.12.2023, 12:59:22

A ,teraz kumam do czego piles piszac te "radosnie" .Nie zakumalem ze to do mnie biggrin.gif
poprostu tak pisze. Jesli ktos robi cos w jednym schemacie, a potem nie z gruszki ni z pietruszki zmienia na cos innego to pisze po prostu "radosnie" . Bez zlosliwosci, ot takie powiedzonko.Tak samo pisze, gdy oceniam komus kod w firmie.
Tak jak ty zawsze zwracales NULL gdy nie bylo rekordu, a potem w jednej funkcji radosnie zwrociles pusty OBJECT.

Jak nie chcesz swojego liba wrzucac do katalug LIB, tylko twardo sie trzymasz VENDOR, to zajrzyj do composera dokumentacji, tam masz podane jak sie importuje prywatne repozytoria z github przez composer. I juz. Ale wywal ten folder VENDOR i wszystko co w nim masz, tak samo jak phpmailer. On jest w composer.json i juz go nie nie powiniinies trzymac w swoim git.

Napisany przez: netir 26.12.2023, 16:02:56

Jak zobaczyłem w model raw query bez bindowania to już przestałem dalej sprawdzać (model nie powinien wiedzieć tyle o implementacji warstwy danych) smile.gif Na pewno jest to ciekawe ćwiczenie do zrozumienia wielu aspektów budowania frameworka i chyba jako ćwiczenie należy to oceniać tongue.gif

  1. $query = "SELECT article.id AS aid, article.image_thumb, article.page_header, article.page_content, section.id AS sid, section.section_name, details.user_id AS uid, details.fullname"
  2. ." FROM dbm_article article"
  3. ." JOIN dbm_article_sections section ON section.id = article.section_id"
  4. ." JOIN dbm_user_details details ON details.user_id = article.user_id"
  5. ." ORDER BY article.created DESC LIMIT $limit";
  6.  
  7. $this->queryExecute($query);

Napisany przez: Malinaa 28.12.2023, 16:04:08

Cytat(nospor @ 21.12.2023, 09:42:45 ) *
1. w ogole na poczatek to zaprzyjaznij sie z takmi narzedziami jak :
- php-cs-fixer
- phpstan
- inne

2. NIe wiem jak planujesz docelowo dostarczac swoj FW, ale na chwile obecna widze wrzuciles go fo VENDOR a ten katalog jest zastrzezony dla COMPOSER. wywal go wiec stamtad i wsadz nie wiem, np do LIBS.

3. No i jesli twoj kod uzywa np phpmailer, ktory jest zapisany w composer.json to nie zapisuj go w GIT. Katalog VENDOR generalnie w GIT ma sie nie znalezc w ogole. po to jest composer

4. plik config.php rowniez powinien nazywac sie np config.php.dist i dopiero ludzie kopiuja go sobie jako config.php lokalnie. Dlaczego? bo z kazdym updatem z twojego gita, ludzie straca swoje zmiany gdy to zostanie jak teraz

5. declare(strict_types=1); ma byc w kazdym pliku php a nie w co drugim

6. Potworki w stylu IF...

7. public function __construct()

8. public function querySql(string $sql, ?string $fetch = null): PDOStatement

9. public function requestData(string $fieldName)

10. public function setDataToDB($value)

11. public function userPermissions(int $user): string

12. public static function temp_htmlUser($sessionUserId, $module = null): void

13. W klasie DatabaseClass
Ty tutaj znowu tworzyc nowy obiekt PDO, czyli nowe polaczenia.

14. public function getSection(int $id): object

15. public function userSigninCorrect(array $params, string $password): ?string

16. public const VALID_LOGIN = 'loginNotFound';


1.
a) php-cs-fixer - jest zainstalowany - pokazuje bez błędów
b ) phpstan - doinstalowałem - pokazuje błąd

Line Controller\IndexController.php
------ ------------------------------------------------------------------------------------
:45 Access to constant BLOG_INDEX_ITEM_LIMIT on an unknown class App\Config\Constants.
💡 Learn more at https://phpstan.org/user-guide/discovering-symbols
------ ----------------------------------------------------------------------------

nie wiem o co chodzi z tym błędem?

c) inne - jakie to jeszcze inne narzędzia

2 i 3. FW dałem do katalogu library.
Usunąłem vendor -> wykonałem composer update i się załadował nowy vendor, czyli powinno być Git (do przetestowania na zdalnym).

Z katalogiem vendor jest pytanie.
Nie jest potrzebny na Gicie, composer go tworzy - Ok, ale co kiedy kod jest w wersji CMS czyli dla zielonych, kiedy zostawię vendor uruchomią aplikacje bez problemu, ale jeśli usunę to nie wykonają polecenia composer i aplikacja nie zadziała. Nie odpalą composera tym bardziej na zdalnym serwerze, ba zwykle taki user nie ma na serwerze SSH, a na pewno nie wie po co to. Zostawiam vendor i problem z głowy, zadziała nawet bez SSH.

4. Dodałem config.php.dist teraz zastanawiam się, czy to coś dało kiedy po zmianie nazwy z config.php.dist na config.php Git i tak będzie widzieć taką zmianę itd. Tu raczej jakiś git ignore config.php trzeba by wstawić?

5. declare(strict_types=1); dopisałem, ale ma być w każdym pliku .php, chyba nie w tempates np. base.html.php ?

6. IFy, może np. tak:

Kod
public function getSections(): ?array
    {
        $query = "SELECT * FROM dbm_article_sections ORDER BY id ASC";

        $this->queryExecute($query);

        if ($this->rowCount() > 0) {
            return $this->fetchAllObject();
        }

        return null;
    }


ponieważ według tego co napisałeś:

Kod
public function getSections(): ?array
    {
        $query = "SELECT * FROM dbm_article_sections ORDER BY id ASC";

        $this->queryExecute($query);

        if ($this->rowCount() == 0) {
            return null;
        }

        return $this->fetchAllObject();
    }

z taką negacją na początku - jasne, że tak się robi, tylko w tym przypadku jakoś mi nie pasuje i nie wygląda najlepiej if count == 0 ? Tu zawsze było if count > 0 !
Jak powinna wyglądać więc ta metoda, tak aby było super git?

7. Nie wiem skąd ten return w konstruktorze - usunąłem.

class DatabaseClass {}

Konstruktor zmieniony, ale czy nie będzie bardziej poprawnie kiedy zamiast DB_HOST odznaczę private $dbHost = DB_HOST; itd. czy bez różnicy, tak i tak jest ok?
Przypomniało mi się, że gdzieś czytałem o exec(); że należy do metod niebezpiecznych i nie raz jest wyłaaczone na serwerze, więc raczej lepiej napisać w wersji z $options = []; ?

8. Ten $fetch wolałbym zostawić, ponieważ przy rozbudowie to nie będzie boolean, można dodać tu więcej opcji niż dwie true/false.

9. public function requestData(string $fieldName) może jakiś pomysł jak poprawić, aby było na 100%?

10. public function setDataToDB($value) czyli do usunięcia.
Czy to tak można zezwalać na zapisywanie wszystkiego do bazy danych co user wklepie w formularz?

11. public function userPermissions(int $user): string - tylko rola, więc komunikaty usuwam.

12. public static function temp_htmlUser($sessionUserId, $module = null): void
Jest oznaczone "temp" czyli tymczasowe do napisania od nowa.
Nie wiem jak wykonać tu połączenie z bazą danych w inny sposób, tzn. pojedyncze?

13. Nie za bardzo wiem jak poprawić całość na takie jedno połączenie z bazą danych?

14. Widzę, że return (object) [] to już kombinacja alpejska, ale

if ($this->rowCount() == 0) {
return null;
}
return $this->fetchObject();

też świetne nie będzie, czy można jeszcze inaczerj (świetnie)?

15. Ta funkcja nie sprawdza czy user zalogowany, a zwraca jesli wystąpi bład komunikat do formularza logowania, czyli jest tu potrzebny string.

16. Miało nazywać się VALIDATION_LOGIN walidacja loginu, ale skróciłem na VALID_LOGIN, zmienione na VALIDATION_LOGIN itd.
Short -> full name - to kosmetyka, czy jest uciążliwe?

Napisany przez: nospor 29.12.2023, 22:02:12

ad1) Nie widzi klasy App\Config\Constants
Jeszcze inne? np. psalm

ad2,3
No niestety, na super zielonych i swiety boze nie pomoze.
Jak bardzo chcesz to dodaj instrukcje skad wziasc biblioteki takie jak phpMailer i zeby wgrac je do katagu vendor. No ale i wowczas caly szlag trafi autoloading itp. wiec generalnie nie przejmuj sie tym.

ad4 tak, config.php ma byc w .gitignore.

ad5) no tylko w plikach klas

ad6 ja podalem ci negacje IFow gdy masz duzy blok kodu w IF. Przyklad co tu pokazales nie lapie sie do tego

ad7. Troche sie zgubilem. Co ma do tego exec() ?

ad9 No nie zakladaj, ze skoro metoda poszla POST, to ze tytko masz dane w post. Jesli metoda POST, to szukaj najpierw w POST, a jak nie ma to zajrzyj tez do GET.

ad10
Jak pisalem, uzywasz bindowania wiec baza jest bezpieczna.
htmlspecialchars() sie uzywa przed wywsietleniem danych a nie przed wlozeniem ich do bazy.
Jesli zas bedziesz mial jakies krytyczne pola, gdzie nie moga pojsc konkretne rzeczy, to tam przed zatwierdzeniem forma, dodajesz VALIDACJE, czy dane sa w porzadku.

ad12,13
Uzyj wzorca SINGLETON na klasie DatabaseClass. I wywal te "Class" z tych wszystkich nazw

ad14
No a co jest zlego w tym?
if ($this->rowCount() == 0) {
return null;
}
return $this->fetchObject();

Jak nic nie ma to zwracasz NULL a jak jest to zwracasz obiekt co chciales. Wiec chyba ok tak?

ad15
No ale to albo zwraca userId albo zwraca komunikat bledu. Wszystko to jest tekstem. Skad ja mam wiedziec czy otrzymany tekst to id uzytkownika i tym samym ok, czy moze to jednak komunikat bledu? No to nie ma sensu. A co oznacza NULL?
Przerob to na funkcje ktore zwraca ID zalogowanego usera albo wyjatek gdy cos nie tak

  1. <?php
  2. public function getSignedUserId(http://www.php.net/array $params, string $password): int
  3. {
  4. $query = "SELECT * FROM dbm_user WHERE (login=:login OR email=:email) AND verified=true LIMIT 1";
  5.  
  6. $res = $this->queryExecute($query, $params);
  7. if (!$res) {
  8. throw new \Exception('Something went wrong');
  9. }
  10.  
  11. if ($this->rowCount() == 0) {
  12. throw new \Exception('Invalid login or email');
  13. }
  14.  
  15. $result = $this->fetchObject();
  16.  
  17. if (!password_verify($password, $result->password)) {
  18. throw new \Exception('Invalid password');
  19.  
  20. }
  21.  
  22. return (int) $result->id;
  23. }
  24. ?>

Zwroc tez uwage jak pozbylem sie tych duzych zagniezdzen co miales. u mnie masz tylko jedno zagniezdzenie a u ciebie byly az 3 podzagniezdzenia. Kod teraz wyglada o niebo czytelniej

ad16
Twoj skrot jest uciazliwy bo tak go skrociles ze nadale przeciwne znaczenie.

Napisany przez: Malinaa 8.01.2024, 09:20:34

1. Jak sprawić, aby widział klasę App\Config\Constants, czy takie umieszczenie stałych w tym pliku jest ok?
Czy plik może zostać w katalogu, w którym się znajduje, czy należy przenieść do katalogu application (autoloader). Wówczas będzie widoczny i wszystko ok.

2 i 3. w katalogu /vendor/ zostawię tylko autoloading i dodałem informacje co jak w README.md

4 - 9.. zrobione

10. sprawdziłem metoda nie była w ogóle używana, więc została usunięta

12 i 13 Jeżeli SINGLETON nie jest zalecane, zróbmy tak jak powinno być z Dependency Injection
Próbowałem zrobić, ale coś nie za bardzo idzie, zacząłem poprawiać od:

Kod
class DatabaseClass extends PDO
{
    //private $connect;
    private $result;
    //public $pdo;
    
    public function __construct() // __construct(PDO $pdo)
    {
        try {
            $dbDSN = "mysql:host=" . DB_HOST . ";dbname=" . DB_DATABASE;
            $dbOptions = [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8",
            ];

            //$this->connect = new PDO($dbDSN, DB_USER, DB_PASSWORD, $dbOptions);
            parent::__construct($dbDSN, DB_USER, DB_PASSWORD, $dbOptions);
        } catch (PDOException $exception) {
            throw new DbmException($exception->getMessage(), $exception->getCode());
        }
    }
...
}


Jedno połączenie wszędzie gdzie tylko jest potrzebne wykonanie zapytania.
Pytanie jak byś to zrobił, aby wstrzyknąć połączenie w plikach i metodach:

Kod
class FrameworkClass
{
...

    // User permissions
    public function userPermissions(int $id): ?string
    {
        $database = new DbmDatabase(); // TODO! Jak zmienic, aby bylo OK?

        $query = "SELECT roles FROM dbm_user WHERE id = ?"; // TODO! Jak to jest z :id lub znakiem zapytania, czy tak samo jest bezpieczne?

        $database->queryExecute($query, [$id]);

        if ($database->rowCount() == 0) {
            return null;
        }

        $data = $database->fetchObject();

        return $data->roles;
    }
}


Kod
class TemplateClass
{
...

    /* TODO! Temporary. Change to full htmlUserNavigation() ? */
    public static function temp_htmlUser($sessionUserId, $module = null): void
    {
        $database = new DatabaseClass();
        $userId = (int) $sessionUserId;
        ...
    }
}


Te Class to trochę tak celowo zostawiłem, aby nad tym chwilę pomyśleć jak powinny nazywać się te klasy oraz folder(y) w obecnym /library/dbmframework/ (folder classes, methods) mając na względzie, że może dodałbym zaciąganie tych klas i kodu do katalogu /vendor/. Poza tym np. ExceptionClass.php nie pasuje zmienić na Exception.php -> class Exception {} podobnie też class Database {} i pozostałe miałby zbyt ogólne nazwy, nie za bardzo to mi się podoba, ponieważ trzeba wówczas dodawać aliasy np. Exception as MyException;. Dodatkowo, czy trzymać wszystko w jednym katalogu, czy może lepiej podzielić te pliki i klasy do subkatalogów i dalej co z namespace też zmienić odpowiedni w zależności, czy dzielimy klasy i kod na subkatalogi? Trochę zastanawiające jak poukładać te pliki i klasy w dobrą całość.

14, Jakoś miałem wrażenie, że
if ($this->rowCount() > 0) { }
jest Ok, bynajmniej logicznie nadal mam takie wrażenie, chociaż bez zagnieżdżenia wygląda lepiej.

15. Metoda ma zwrócić string, nie int i służy do wyświetlania komunikatów w formularzu, więc nie może być throw.
Pozbyłem się tych zagnieżdżeń i wygląda lepiej.

16. Dla jasności zmienione na bez skrótów, które jak się okazuje mogą być mylące.

Mam prośbę o pomoc w ogarnięciu Dependency Injection w projekcie, bo się przy tym już pogubiłem.

Napisany przez: nospor 8.01.2024, 10:11:34

ad1 Klasy maja byc widoczne w autoloading i wtedy powinny byc widoczne przez analizatory.
Swoja droga pisales ze zadnych innych bledow ci nie pluly. Troche dziwne, bo przegldajac twoj kod powinno wypluc wiecej bledow. Na pewno dobrze to skonfigurowales by patrzylo na wszystkie katalogi/pliki?

ad15
Jesli twoja metoda jako tekst zwraca albo id uzytkownika, albo komunikat bledu, to jest to zle zaprojektowana metoda. juz ci to pisalem. wiec sie zdecyduj co ona ma zwraca, albo komunikat bledu, albo id uzytkownika.
Zas co do throw to po to masz TRY CATCH by to wlasnie tam zlapac

ad13:
no Singleton w twoim miejscu bylby najlepszy. Najszybciej bys go wdrozyl. Ale jesli nie chcesz to o to:
$database = new DbmDatabase();
masz zrobic tylko raz w swojej aplikacje, w jakims glownym core, chociazby w index.php i potem te $database masz przekazywac do kazdych innych klas (w konstruktorze) ktore tego potrzebuja. Ale twoj kod nie jest na to przygotowany wiec dlatego juz lepiej bys uzyl Singleton

Napisany przez: Malinaa 8.01.2024, 13:28:12

1. Klasa przeniesiona i widoczna - całość bez błędów.

Narzędzia sprawdzające jakość kodu i błędy nie pluły mi błędów poza tym jednym co się powtarzał x-razy o klasie która jest nie widoczna
oraz drobiazg, o którym nie pisałem, bo ogólnie wystarczyło przenieść echo() do nowej linii.

15. Ok, ale zostawiam tę metodę, aby nie musieć pisać dwóch, ponieważ to id jest używane tylko w session value i jest stringiem.
Taka nie typowa metoda, na pewno słyszałeś 2 in 1 smile.gif

13. Rozumiem, że byłby lepszy, ale to powinno być po prostu super dobrze.

Czyli w pliku głównym index.php tu gdzie mam kod Routa dodaję Database:

Kod
### RENDER PAGE ###
use Dbm\Classes\RoutClass;
use Dbm\Classes\DatabaseClass; // as DbmDatabase;

new RoutClass();

$database = new DatabaseClass();


i jak dalej przygotować ten mój kod pod DI, ponieważ nie pomyślałem o tym na początku, a teraz przy tym DI to już się zamotałem.

Napisany przez: nospor 8.01.2024, 14:27:04

Cytat
ale to powinno być po prostu super dobrze.

Z jednej strony nalegasz na super dobrze, a z drugiej strony piszesz metode ktora zwraca albo ID albo komunikat bledu, wszystko jako tekst i czort wie czy to co zwrocilo to id czy moze jednak error i sie upierasz ze tak ma byc wink.gif

Singleton u ciebie bedzie OK bo caly kod nadal nie jest super dobrze.

No ale jak sie upierasz to w FrameworkClass w metoddzie model() masz
return new $modelNamespace();
tam wiec do constructora masz przekazac $database
A skad FrameworkClass ma miec $database? No to widze kontroller dziedziczy po FC wiec w RoutClass masz przekazac do kontrolera w konstruktorze $database.
A skad RoutClass ma miec $database? No widze ze w index.php tworzysz obiekt RoutClass wiec tam masz przekazac jeszcze $database

I tak z kazda sciezka gdzie $database jest wykorzystywane

  1.  
  2. $database = new DatabaseClass();
  3. new RoutClass($database);




Albo zaimplementuj Registry, wloz raz $database do registry i mozesz pominac pare krokow.
Albo zastosuj singleton wink.gif

edit: wlasnie sobie uswiadomilem ze i tak ta cala sciezka z $database co ci teraz podalem jest o kant 4 liter skoro u ciebie kazdy model dziedzicy po DatabaseClass a ta w konstruktorze na dzien dobry tworzy nowe polaczenie z baza
Dlatego nic juz niekombinuj, tylko utworz ten singleton na klasie DatabaseCLass i problem z glowy
Tylko zeby zrobic z tego Singleton, to wywal to dziedziczenie po PDO co widze dodales ostatnio w klasie DatabaseClass rob normalnie new PDO itp. Oczywiscie okraszone juz Singletonem


edit2:
A tu masz pokaze ci jeszcze jak sie pozbywac tych nieszczesnych zagniezdzen
Aktualnie masz taka metode (przeczytaj komentarze co tam wlozylem)
  1. public function queryExecute(string $query, ?http://www.php.net/array $params = [], bool $reference = false): bool
  2. {
  3. try {
  4. $this->result = $this->prepare($query);
  5.  
  6. if (http://www.php.net/empty($params)) {
  7. return $this->result->execute();
  8. } else {
  9. // PO CO TEN ELSE? Przeciez IF mial RETURN wiec ELSE NIEPOTRZEBNY
  10. $first = array_key_first($params);
  11.  
  12. if (http://www.php.net/is_string($first)) {
  13. // PO CO ten dlugi kod w tym IF? Nie lepiej zrobic negacje i w IF tylko wlozysz jeden prosty return?
  14. foreach ($params as $key => &$value) {
  15. http://www.php.net/is_int($value) ? $type = PDO::PARAM_INT : $type = PDO::PARAM_STR;
  16.  
  17. if (!$reference) {
  18. $this->result->bindValue($key, $value, $type);
  19. } else {
  20. $this->result->bindParam($key, $value, $type);
  21. }
  22. }
  23.  
  24. return $this->result->execute();
  25. }
  26.  
  27. return $this->result->execute($params);
  28. }
  29. } catch (PDOException $exception) {
  30. throw new DbmException($exception->getMessage(), $exception->errorInfo[1]);
  31. }
  32. }


A tu po poprawkach, Zniknely dwa zbedne zagniezdzenia i od razu czytelniej.
  1. public function queryExecute(string $query, ?http://www.php.net/array $params = [], bool $reference = false): bool
  2. {
  3. try {
  4. $this->result = $this->prepare($query);
  5.  
  6. if (http://www.php.net/empty($params)) {
  7. return $this->result->execute();
  8. }
  9.  
  10. $first = array_key_first($params);
  11.  
  12. if (!http://www.php.net/is_string($first)) {
  13. return $this->result->execute($params);
  14. }
  15.  
  16. foreach ($params as $key => &$value) {
  17. http://www.php.net/is_int($value) ? $type = PDO::PARAM_INT : $type = PDO::PARAM_STR;
  18.  
  19. if (!$reference) {
  20. $this->result->bindValue($key, $value, $type);
  21. } else {
  22. $this->result->bindParam($key, $value, $type);
  23. }
  24. }
  25.  
  26.  
  27. return $this->result->execute();
  28.  
  29. } catch (PDOException $exception) {
  30. throw new DbmException($exception->getMessage(), $exception->errorInfo[1]);
  31. }
  32. }

Napisany przez: Malinaa 9.01.2024, 13:01:28

Cytat
Z jednej strony nalegasz na super dobrze, a z drugiej strony piszesz metode ktora zwraca albo ID albo komunikat bledu...


Kod znajdujący się w folderze /library/dbmframework/ oraz bezpośrednio z nim powiązany nalegam, aby napisać super dobrze.
Metoda o której piszesz bardziej jest związana z wersją demo (cms), nie jest jakaś super kluczowa, to tylko demo, jak coś będzie tu zakombinowane w stylu 2w1 - no problem.
Element demo cms - na luzie. Jak to wytłumaczyć, może spójrz na to w ten sposób, że przy kolejnym projekcie opartym o dbmframework folder /application/ będzie pusty, wszystko co jest w tym folderze będzie od nowa tworzone w nowej wersji (nie cms).
P.S. Przykładowo mam skrypt płatności, który napisałem 10 lat temu, przydałoby się go napisać od nowa i chciałbym oprzeć projekt od library ->dbmframework. Czy teraz kaman? Kaman beauty smile.gif

Uparłeś się na Singleton, więc zacząłem pisać pod ten Singleton:

Kod
class DatabaseClass // implements SingletonInterface - * Przyszły mi tu na myśl interfejsy, fajnie byłoby dodać takie do aplikacji np. w /library/dbmframework/interfaces/ pytanie jak tworzyć interfejsy, które później można fajne wstrzykiwać np. do konstruktora itp. itd.
{
    private static $instance = null; // static - po dodaniu pierwszego elementu dla Singletona zaczyna się już jakaś katastrofa !?
    private $connect;
    private $statement;

    //private function __construct() // po zmianie na private - wysypie się
    public function __construct()
    {
        try {
            $dbDSN = "mysql:host=" . DB_HOST . ";dbname=" . DB_DATABASE;
            $dbOptions = [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8",
            ];

            $this->connect = new PDO($dbDSN, DB_USER, DB_PASSWORD, $dbOptions);
        } catch (PDOException $exception) {
            throw new DbmException($exception->getMessage(), $exception->getCode());
        }
    }

    public static function getInstance() //: Result ?
    {
        if (!self::$instance) {
            self::$instance = new DatabaseClass();
        }

        return self::$instance;
    }

    public function getConnection() //: Result ?
    {
        return $this->connect;
    }
}


Przy Singleton o ile widzę tworzymy tu zmienne globalne - to nie może tak być, okropnie wygląda, nie róbmy tego.

Modele, o których piszesz jak widzę i tak muszą zostać poprawione (przerobione), coś nie sposób w nich wstawić chociażby konstruktora.

Kod
class BlogModel extends DatabaseClass // TODO! Remove extends DatabaseClass
{
    /* TODO! private $database;

    public function __construct(DatabaseClass $database)
    {
        $this->database = $database;
    } */
..
}


W kontrolerach jest podobnie lipa, brak możliwości wstrzykiwania do konstruktora

Kod
class IndexController extends FrameworkClass
{
    private $blogModel;
    private $translation;

    /*
     * TODO! public function __construct(TranslationClass $translation)
     */
    public function __construct()
    {
        $this->blogModel = $this->model('BlogModel'); // TODO! Zmienic sposob ladowania ClassModel ?!

        $translation = new TranslationClass();
        $this->translation = $translation;
    }
...
}


gdzie np. w Symfony jest AbstractController i można do konstruktora wstrzykiwać co dusza zapragnie - super.

Prośba, nalegam wink.gif zróbmy Dependency Injection.

Dzięki za poprawki i komentarze w queryExecute().
Przy if ($this->rowCount() == 0) {} miałem mieszane wrażenia, zawsze bardziej pasowało mi tu if ($this->rowCount() > 0) {} ale bez zagnieżdżeń jest lepiej.
W przesłanym przykładzie akurat bez dwóch zdań po co te Ify teraz jest dobrze smile.gif

Napisany przez: nospor 9.01.2024, 14:16:56

NIe wiem o co ci chodzi z zmiennymi globalnymi w SINGLETON, skoro SINGLETON nie tworzy zmiennych globalnych


Olej singleton, popraw sobie troche te DatabaseClass na

  1. class DatabaseClass
  2. {
  3. protected http://www.php.net/static $connect;
  4.  
  5.  
  6. public function __construct()
  7. {
  8. try {
  9.  
  10. if (!self::$connect) {
  11. $dbDSN = "mysql:host=" . DB_HOST . ";dbname=" . DB_DATABASE;
  12. $dbOptions = [
  13. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  14. PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8",
  15. ];
  16.  
  17. self::$connect = new PDO($dbDSN, DB_USER, DB_PASSWORD, $dbOptions);
  18. }
  19. } catch (PDOException $exception) {
  20. throw new DbmException($exception->getMessage(), $exception->getCode());
  21. }
  22. }
  23. // reszta co miales gdzies tam
  24.  
  25. }

I juz. Teraz sobie mozesz robic i milion razy new DatabaseClass() i Modeli z tego dzieciczacych a i tak ci zrobi tyklo jedno polaczenie PDO. I juz

Co do prawidlowego DI to trzeba by u cibie w pyte zmieniac a ja nie mam czasu teraz nad tym myslec. Chyba ze inna dobra dusza sie zdecuduje za to brac

Napisany przez: Malinaa 9.01.2024, 20:07:04

Z protected static $connect to takie zakombinowane, lepiej bo jak rozumiem mamy teraz jedno połączenie, ale czy jest dobrze?!

Innych dusz kumatych to coś nie widać, więc daj znać jakbyś znalazł trochę czasu.

Napisany przez: nospor 10.01.2024, 09:55:45

Cytat
Z protected static $connect to takie zakombinowane, lepiej bo jak rozumiem mamy teraz jedno połączenie, ale czy jest dobrze?!

No jest troche zakombinowane, ale czasami trzeba podejsc do tematu pragmatycznie. Na chwile obecna to bedzie dobrze, bo dzieki temu unikasz tworzenia kilku polaczen do tej samej bazy danych wiec maszz poprawie o 470%* w porownaniu do tego co bylo wink.gif


Cytat
nnych dusz kumatych to coś nie widać, więc daj znać jakbyś znalazł trochę czasu.

ok



*tak wiem, wartosc wzialem z powietrza ale poprawa i tak jest

Napisany przez: viking 10.01.2024, 12:22:37

https://github.com/artimman/dbmframework/blob/main/config/_RENAME_TO_PHP_config.php.dist
Dlaczego zamiast użyć https://github.com/vlucas/phpdotenv to masz tego potwora?

w .gitignore masz vendor a jednak jest na serwerze.
Trochę mi się nie podoba że kontrolery nic nie zwracają, a sama metoda view robi include i na tym koniec. Takie wrażenie niezamkniętego requestu.

Napisany przez: Malinaa 11.01.2024, 11:55:06

Cytat(nospor @ 10.01.2024, 09:55:45 ) *
No jest troche zakombinowane, ale czasami trzeba podejsc do tematu pragmatycznie...


Podejście pragmatyczne w programowaniu, brzmi dość filozoficznie, być może przekracza moje doświadczenia. Wystarczy, że skoncentrujemy się na wspomnianej abstrakcji smile.gif

* z powietrza? To chyba jakiś chwyt marketingowy, 470% kiedy 100% to max, może 47%, ewentualnie 4,7 tzn. 5 razy lepiej
i poprawa widoczna gołym okiem smile.gif

Cytat(viking)
config.php.dist - potwór?


1. Przy instalacji należy zmienić nazwę na config.php - nie wiem dlaczego to jest potwór?
2. O ile rozumiem chcesz zamienić config.php.dist na .env
Podałeś jednak od razu link na jakąś petardę, może jakiś przykład dla zielonych (nie git gotowiec, a czysty kod), bo mam mieszane uczucia, nie wiem co chcesz zrobić.
3. Tak racja (musiałem podejść do tematu pragmatycznie), z katalogiem vendor rzecz zrobiła się bardziej złożona, mogę próbować wytłumaczyć jeśli trzeba, ale przeczytaj README.md co powinno rozjaśnić kłopot. którym teoretycznie można by się nie przejmować... ale odbiorca (klient) jest bardzo istotny.

Też mi się trochę nie podoba rozwiązanie z kontrolerami i metodami (do poprawki), ale nie mam coś pomysłu na lepsze.
Jak masz pomysł na poukładanie kodu w stabilną całość - podaj koniecznie rozwiązanie problemu, a będę mówił Ci Królu przez cały tydzień.

Napisany przez: nospor 11.01.2024, 12:03:10

Cytat
Podejście pragmatyczne w programowaniu, brzmi dość filozoficznie,

A ja jakos w robocie dosc czesto musze do tematu podchodzic pragmatycznie bo zrobienie czegos w sposob podrecznikowy, poprostu zajmie 10x dluzej czasu, albo bedzie wolniejsze albo to albo tamto.Takze wiedza wiedzą ale umiejetnosc adaptacji do panujacych warunkow tez wazna wink.gif

Napisany przez: Malinaa 11.01.2024, 12:36:08

Cytat(nospor @ 11.01.2024, 12:03:10 ) *
A ja jakos w robocie dosc czesto musze do tematu podchodzic pragmatycznie...

Z filozoficznego punktu widzenia:
" Wyobraź sobie że życie polega na żonglowaniu pięcioma piłkami. Ich nazwy to praca, rodzina, zdrowie, przyjaciele i prawość. Wszystko udaje ci się utrzymywać w powietrzu. Ale pewnego dnia wreszcie do ciebie dociera, że praca jest gumową piłką. Jeżeli ją upuścisz, odbije się i wróci. Pozostałe cztery piłki - rodzina, zdrowie, przyjaciele, prawość - to szklane kule. Jeśli się którąś upuści, może się obić, wyszczerbić lub nawet roztrzaskać.
smile.gif

No, ale wracamy na polską ziemię 10x szybciej, jeśli środowisko tego wymaga itp. itd. Jasne.

Napisany przez: viking 11.01.2024, 12:39:55

Cytat(Malinaa @ 11.01.2024, 11:55:06 ) *
1. Przy instalacji należy zmienić nazwę na config.php - nie wiem dlaczego to jest potwór?
2. O ile rozumiem chcesz zamienić config.php.dist na .env
Podałeś jednak od razu link na jakąś petardę, może jakiś przykład dla zielonych (nie git gotowiec, a czysty kod), bo mam mieszane uczucia, nie wiem co chcesz zrobić.
3. Tak racja (musiałem podejść do tematu pragmatycznie), z katalogiem vendor rzecz zrobiła się bardziej złożona, mogę próbować wytłumaczyć jeśli trzeba, ale przeczytaj README.md co powinno rozjaśnić kłopot. którym teoretycznie można by się nie przejmować... ale odbiorca (klient) jest bardzo istotny.

Też mi się trochę nie podoba rozwiązanie z kontrolerami i metodami (do poprawki), ale nie mam coś pomysłu na lepsze.
Jak masz pomysł na poukładanie kodu w stabilną całość - podaj koniecznie rozwiązanie problemu, a będę mówił Ci Królu przez cały tydzień.


Większość osób które chciałyby wrzucić taki config na swojego gita pewnie puści to jak leci zapisując w nim wszystkie hasła do systemu. .env jest uniwersalne, powinno być wykluczone z commita. Są też różne środowiska. Np osoba pracująca z kontenerami może chcieć zaczytać env do kontenera podczas uruchamiania swojego stacka. Poza tym nie lubię jak wszędzie pałętają się jakieś stałe.
Przykładów jak używać masz pełno, np https://github.com/vlucas/phpdotenv#immutability-and-repository-customization

Dlaczego mam czytać readme żeby dowiedzieć się coś o vendor? Tego folderu nie ma prawa być. Są różne wersje php, OS, różne zależności. Tych plików nie wrzuca się ot tak.

Poprawny kod powinien być zgodny z https://www.php-fig.org/psr/psr-15/

Nie rozumiem nigdy tego pisania własnego FW przez początkujących. Nie, nie jest to dobra metoda bo nabierasz złych nawyków, nie masz kogoś, kto nad tobą stoi i wytyka cały czas błędy. Poużywaj trochę mainstreamowe fwameworki, zobacz jak w nich robi się pewne rzeczy. A jak już będziesz na poziomie pro wtedy można coś myśleć (chociaż i tak nie ma to sensu).

Napisany przez: Malinaa 11.01.2024, 13:19:54

Cytat(viking @ 11.01.2024, 12:39:55 ) *
Większość osób które chciałyby...


Aby większość osób nie wrzuciła config z hasłami na gita dodany został plik config.php.dist natomiast config.php jest .gitignore, więc nie wiem dlaczego - potwór.
Na pewno lepiej będzie przerobić na .env - dzięki za info.

Nie chcesz przeczytać, ok. Przecież nie spodziewałem się, że chcesz, więc tłumaczenie mamy z głowy.

Co tu rozumieć, potrzebny jest kod w czystym PHP do prostych aplikacji, czyli wzorzec MVC (frameworkowy) do takich systemów jest lux, nadaje się do systemu CMS.
Poziom pro, ponownie nie wiem co masz na myśli, czy też co chcesz zrobić, albo czego nie chce Ci się robić, poza tym co lub kogo lubisz, może nie lubisz... "chociaż i tak nie ma to sensu",
więc abyśmy nie pisali bez sensu, może zapytam krótko: co Twoim zdaniem ma sens?

Napisany przez: viking 11.01.2024, 13:33:16

Przeczytać przeczytałem ale napisałem dlaczego to jest bez sensu. Vendor i zależności tworzone są w zależności od OS i wersji php. To że sobie coś wrzucisz u kogoś innego nie oznacza z góry że to zadziała. Sam php mailer ma np zależności

Cytat
"ext-ctype": "*","ext-filter": "*","ext-hash": "*"
U kogoś nie będzie i już nie działa. Nie dowiesz się tego bez instalacji, sypnie błędem w którymś momencie wykonywania kodu.

Wzorzec MVC jest standardem i owszem wszędzie się nada. Ale jego implementacja robi różnicę. Jest od groma różnych frameworków. Przykładowo https://www.slimframework.com/ wchodzę na stronę i widząc go pierwszy raz wiem jak dodać kolejną podstronę. A to dlatego że przestrzega reguł (psr). Wiem że ma testy i dużo osób od lat go poprawia. U Ciebie nie dość że trzeba się przekopać przez kod który tylko Ty rozumiesz to nie ma żadnej gwarancji że on działa poprawnie.

Laravel 11 też dąży do tego aby na start było jak najmniej, do tego ma gigantyczną społeczność i pełną integrację z różnymi projektami zewnętrznymi (alpine, tailwind itp). Znów ogromna ilość testów, osób które nad nim pracują i fajne rozwiązania których nigdy nie wdrożysz u siebie (nawet to ograniczenie php 7.3 gdzie mamy już 8.3).

To tylko dwa przykłady. I żeby nie było, dobrze że piszesz coś tylko IMO najpierw popracuj z innymi FW, zobacz jak coś jest zrobione i na pewno dojdziesz do wniosku że to co napisałeś jest do zaorania (też dobrze).

Napisany przez: Malinaa 11.01.2024, 14:01:52

Rozumiem, mimo to chciałbym opracować mechanizm (w wersji stabilnej) oparty na wzorcu MVC (chyba już z czystej ciekawości), ale dzięki za wytykanie błędów.
IMO? Wyobraźnia jest nieograniczona, reszta to taki chwyt marketingowy, w którym nowości zawsze wygrywają (to kwestia czasu kiedy inne FW też zostaną zaorane) wink.gif

Napisany przez: viking 11.01.2024, 14:32:23

To na początek ja bym spróbował ogarnąć temat z którym masz tutaj taki problem czyli rozwiązanie z singletonem. Spróbuj tak przepisać kod żeby zaimplementować jakiś gotowy system dependency injection np https://php-di.org/ (propozycja losowa). I poczytaj chociaż dokumentację innych fw żeby zobaczyć co można robić i w jaki sposób.

Napisany przez: Malinaa 11.01.2024, 22:42:39

Wydawało mi się, że takie powszechne rozwiązanie jak framework oparty na wzorcu MVC na moje potrzeby (wspomniany CMS i proste aplikacje) będzie najlepszym rozwiązaniem, a teraz to już nie wiem,
tzn. wiem, że nic innowacyjnego nie wymyślam i pewnie nic innowacyjnego tu już nie wymyślę, jeżeli masz jakiś genialny pomysły to fajnie byłoby zastosować, nie wiem co mam powiedzieć -> łap okazje -> będzie można się czymś wyróżnić.

Problematyczny temat to już próbowałem ogarnąć, nie był to początek, bardziej wygląda na koniec, ale tak jak będzie jeszcze na to czas, spróbuję z dependency injection,
wszak chciałbym wiedzieć jak to się dzieje, że taka "żarówka" świeci i ją zbudować, chociaż większości osób wystarcza on/off i też są happy.

P.S. Dokumentacje w j. ang. można czytać, jeśli ma być ze zrozumieniem to na 50% zobaczę co można robić i w jaki sposób, czyli trafię bankowo w najciekawsze kawałki.

Napisany przez: com 18.01.2024, 14:49:35

Malinaa musisz sobie postawić zasadnicze pytanie poco to tworzysz? Kiedy będziesz szukał potencjalnej pracy kiedyś zakładam na pewno to nastąpi zapytają Cie jakie frameworki znasz, nie powiesz im przecież, że tylko "DbM Framework" bo nikt tego poza Tobą nie zna. Dziś startując na rynku pracy większość ofert wymaga od pracownika znajomości jednego z fw: Symfony, Laravel itp. Jakaś bazę wiedzy już zebrałeś i większość z nas pisała kiedyś własny framework albo swojego cmsa, bo kiedyś to było modne, czasy się zmieniły, dziś nie ma to już sensu, a tworzenie takiego czegoś często uczy Cie od razu z automatu złych praktyk, bo nie masz żadnego mentora który wytyka błędy, nospor i viking dotknęli tylko szczytu góry lodowej przeglądając to w mniej czy bardziej dokładny sposób, ale dziś to nie wystarczy. Podstawami do nauki PHP od zawsze była dokumentacja, ale oprócz tego przez lata wypracowane zostały pewne standardy oznaczone jako PSR https://www.php-fig.org/psr/ i jeśli tworzysz oprogramowanie i się do nich nie stosujesz to od początku popełniasz błąd, który w próżniejszym czasie będzie Cie kosztował nauka złych nawyków których nie będziesz się w łatwy sposób w stanie pozbyć. Polecałbym poczytać np coś takiego https://phptherightway.com/ i zamiast przepalać własne godziny na tworzenie własnego frameworka bez wiedzy i doświadczenia, skupić się na próbie zrozumienia jak działa jeden z powszechnych i dostępnych fw na rynku. A jeśli masz ambicja do tego żeby stworzyć własnego CMSa jako taki pet project dla samorozwoju, to właśnie oprzyj go na takim Symfony czy Laravel, dzięki temu uda Ci się zrozumieć jak to działa i nie będziesz uczył się złych praktyk, a poznasz wiele dobrych praktyk, szczególnie jeśli chodzi o Symfony to nie ma tam antywzorców itp Troche gorzej jest w Laravel, oczywiście da się je wyeliminować ale to trzeba być już na wyższym poziomie wtajemniczenia, niemniej jednak nawet użycie Laravel będzie miało równie wymierne korzyści(po prostu Laravel posiada/posiadał takie anty wzorce jak Active Record(można go zastąpić przez jakiś Data Mapper np Doctrine) oraz nadmiernie wykorzystywał wzorzec Fasady, ale pozostałe kwestie są tam na najwyższym poziomie). Do nauki polecam https://symfonycasts.com/ lub https://laracasts.com/. Na przyszłość jak już tworzysz własny Fw to jest coś takiego jak https://symfony.com/doc/current/create_framework/index.html i należało by się zastosować do tych zasad opisanych tam żeby to miało jakikolwiek sens smile.gif

Kiedy już opanujesz podstawy wybranego przez siebie fw, bez problemu jeśli trafisz na jakiś problem przy tworzeniu tego własnego CMSa znajdziesz tutaj na forum i na innych masę osób które znają ten fw na wylot i beda w stanie Ci pomoc, nakierować itp, nikt tego nie napisze za Ciebie ale to będzie dla Ciebie merytorycznie znacznie korzystniejsze niż ocenianie wielu godzin twojej pracy nad "DbM Framework", bo jakby się defakto nad tym mocno zastanowić i zastosować do PSR i innych dobrych praktyk to ten kod cały by trzeba napisać na nowo smile.gif Nie chcę Cie zrazić i nie odbierz tego jako hejt, to normalne kiedy osoba która się uczy zabiera się za tworzenie FW/CMS, a na jej kod spojrzy ktoś z dużo większym stażem, tez kiedyś zaczynałem i popełniłem taki sam projekt, zarówno robiłem własnego CMSa jak i fw w swojej karierze(ale dopiero pierwszy komercyjny projekt zrobiony w Symfony, nawet po latach jak do niego zajrzałem, to mimo iż dziś posiadam o wiele więcej doświadczenia niż wtedy, ale budowałem go zgodnie z dokumentacja na SynfonyCast wtedy defa kto nazywało się jeszcze KnpUniversity, to jak patrze na ten kod po latach to on nadal nie wymaga zmian, no możne poza tym że dziś bym dodał typy zmiennych wszędzie gdzie się tylko da, za to jak zaglądam do swojego cmsa/fw to widzę jak mało jeszcze wtedy znałem te technologie), ale to było ponad 15 lat temu, dziś bym się tego nie podjął a od początku po przestudiowaniu dokumentacji dla PHP zacząłbym eksperymenty pewnie z Symfony smile.gif Tobie również życzę by "DbM Framework" pozostało historia do której wrócisz za 10 lat i zobaczysz jaki przeskok w swojej wiedzy za ten czas zyskałeś i żeby pierwszy twój projekt w fw jaki sobie wybierzesz nadal za te 10 lat cieszył oko, a nawet jakby było tam cos do poprawy to żebyś mimo wszytko dostrzegał to jaka przepaść będzie dzielić cms na fw "DbM Framework" a na ogólnodostępnym fw, ale to myśle już teraz zauważysz jeśli spróbujesz pójść w te stronę smile.gif Bo jeszcze przed Tobą poznanie czym jest DDD i tworzenie kody który będzie Framework-agnostic, bo fw to jest taki Ninja Master toolbox(scyzoryk, pila, klucze w jednym) i jest on w miarę uniwersalny ale on powinien być tylko jednym z narzędzi ułatwiającym prace tak jak IDE jest dla pisania kodu i powinien pomagać a nie nas ograniczać smile.gif ale to gdzieś tam daleko w przyszłości zrozumiesz o czym mówię smile.gif

PS na koniec dodam jeszcze że w protokole HTTP nie da odzwierciedlić wzorca MVC, tutaj tworzona jest tylko jedna z wariacji bazująca na tej idei, ale nigdy nie będzie to pełnoprawny MVC, wiec w sumie powoli przestaje się tak nawet nazywać te fw(Jeśli przeczytasz o Symfony na nie polskiej stronie Wikipiedii, bo tam nikt tego nie redaguje to nie ma już nic wzmianki o MVC) wink.gif

Napisany przez: Malinaa 18.01.2024, 21:38:13

Cytat(viking @ 11.01.2024, 14:32:23 ) *
Spróbuj tak przepisać kod żeby zaimplementować jakiś gotowy system dependency injection

viking piszę, że nie chcę żadnego gotowca, a na siłę podajesz gotowca za gotowcem.

Potrzebuję mechanizm, który działa bez Composera, Composer ma być opcjonalny, można użyć, ale nie ma takiej konieczności, aby uruchomić aplikacje typu CMS, albo coś prostszego ma wystarczyć wypełnienie formularza instalacyjnego z danymi typu login, hasło, serwer, baza danych -> klikamy Go i gotowe, aplikacja śmiga (bez żadnego composer install). W przypadku części kodu napisanej pod Landingpage obyłoby się też bez wypełniania formularza instalacji.
Co mi tu brakuje, aby poprawnie poukładać tych kilka plików w folderze library/dbmframework:
1. Wspomniane DI, które nie za bardzo wiem jak napisać dobrze w czystym kodzie (bez gotowca) tak, aby dało się używać w całej aplikacji.
2. Problem z kontrolerami i modelami, przydałoby się więcej abstrakcji i wstrzykiwanie, ale to jest powiązane z DI.

Wspomniałeś o katalogu vendor, że nie może w nim być nawet autoloadingu, nie ma go być w ogóle - kod poprawiony vendor usunięty, aplikacja działa bez vendora i Composera i już jest tak jak chciałem, a jak zechcesz użyć Composera to też wystarczy, że wykonasz composer install i będzie chodzić z Composerem.
Usunięty katalog vendor i var dodane do gitignore (program utworzy sobie var'a a composer vendor).

config.php - nie jest błędem, więc na ten czas podarujmy sobie .env, oki.

PSR 15 i Interfejsy - Królu naucz pisać porządne interfejsy, bo z interfejsami to mam zielono.
Czy takie tam Psr\Http\Server\RequestHandlerInterface można zastosować bez Composera? Jak to, kiedy musi być bezbłędnie smile.gif

SlimFramework jest Ok, bliski temu co przyszło mi na myśl, ale Composer jest tu niezbędny podobnie jak w każdym innym FW co nie spełnia mini wymogu - Mechanizm (DbM Framework) ma działać na czystym kodzie bez pakietów, bibliotek, Composera itd.
Poza tym SlimFramework pisany i poprawiny jest od kilkunastu lat, ma swoją społeczność itd. przy kodzie DbM FW posiedziałem w zeszłym roku przez jakiś miesiąc tworząc v1 i teraz drugie podejście i powstało v2 - no bez sensu byłoby zabierać się dzisiaj za kolejny framework i liczyć na oklaski, ale potrzebny jest wspomniany mechanizm na minimalistycznym, bezbłędnym kodzie.

Cytat
Malinaa musisz sobie postawić zasadnicze pytanie poco to tworzysz?

Pan Bóg stworzył istotę dla hecy, z tyłu plecy, z przodu plecy. A jednak po niej przyszedł ten, który był zrodzony, a nie stworzony smile.gif
Chcesz pomóc? Napisz jak mam zakodować DI i inne drobiazgi, o których pisałem wyżej.

Napisany przez: Salvation 23.01.2024, 10:21:20

Cytat(Malinaa @ 18.01.2024, 21:38:13 ) *
(...) ma wystarczyć wypełnienie formularza instalacyjnego z danymi typu login, hasło, serwer, baza danych -> klikamy Go i gotowe, aplikacja śmiga (...)

No to wrzuć vendora do paczki instalacyjnej od razu. Tak robi wiele "gotowych" systemów. A composera i tak wdróż, bo niepotrzebnie wymyślasz koło na nowo.
Chyba, że chcesz się jedynie nauczyć tego co "pod maską", bo w przeciwnym razie ten autorski framework będzie tylko do szuflady.

Już znam firmy co wrzucają klientów na własny fw (swoją drogą, ciekawy model biznesowy), bo tworzenie go zajęło tyle, że jeszcze się nie spłacił.

Napisany przez: Tomplus 24.01.2024, 09:31:42

Ja osobiście rozumiem dlaczego ktoś tworzy i stosuje własne FW. Mają kilka zalet:
- na początku są proste w obsłudze
- są lekkie
- uwzględniają specyfikę hostingu
- są szyte na miarę umiejętności twórcy.

Jednak problemy przychodzą gdy, projekt się rozrasta i różnych powiązań modułów jest coraz więcej. Zaczynają pojawiać się zbędne powtórzenia, blędy tam gdzie nie powinno ich być.

Napisany przez: Malinaa 27.01.2024, 20:29:21

Mam chwilę, więc usiadłem do projektu. Dependency injection z tego co widzę tak szybko nie zrobię, właściwie to trzeba przerobić całość w katalogu /library/dbmframework/
https://github.com/artimman/dbmframework a następnie kontrolery itd.
a co gorsza nie mam pomysłu jak zrobić, aby było Git.

Cytat(Salvation @ 23.01.2024, 10:21:20 ) *
No to wrzuć vendora do paczki instalacyjnej od razu. Tak robi wiele "gotowych" systemów. A composera i tak wdróż, bo niepotrzebnie wymyślasz koło na nowo.
Chyba, że chcesz się jedynie nauczyć tego co "pod maską", bo w przeciwnym razie ten autorski framework będzie tylko do szuflady.
Już znam firmy co wrzucają klientów na własny fw (swoją drogą, ciekawy model biznesowy), bo tworzenie go zajęło tyle, że jeszcze się nie spłacił.


Z informacji ma być bez katalogu vendor!?
Zrobiłem tak, że w pliku index.php dodałem funkcje autoloadingWithWithoutComposer($pathAutoload); i rozpoznaje, czy został uruchomiony Compser, jeśli tak to autoloading leci z Composera, jeśli nie to dopisałem własny autoloadnig, któet wczytuje wszytsko co potrzeba bez Composera. Takie rozwiązanie nie jest chyba wymyślaniem koła od nowa? Jeżeli w tym rozwiązaniu coś nie gra, no to pytanie jak wrzycić vendora do paczki instalacyjnej, mając na względzie, ze pewnie ten vendor będzie generowany innaczej w zależości o wersji PHP.

Projekt ten ma charakter takiego "poligonu ćwiczebnego", nauczenia się tego co jest "pod maską", ale będzie super Git jeśli uda się wyciągnąć z niego coś więcej niż kod zaszufladkowany tym bardziej, że po prostu jest mi taki kod potrzebny, problem, aby napisać go bezbłędnie smile.gif

Tak daleko nie patrzyłem, ale ciekawy model biznesowy.

Cytat
Ja osobiście rozumiem dlaczego ktoś tworzy i stosuje własne FW.


Pocieszające słowa, bo już myślałem, że porywam się na coś co każdemu wydaje się być bez sensu i głupie, że aż głupio gadać.
Jeden poza oczywiście nosporem, który potrafi motywować smile.gif (nie zniechęcać).

Usiadłem do tego DI, ale już przy pierwszym uruchomieniu PHP-CS-Fixer pojawił się kłopot z szablonami, doszedłem do wniosku, ze projekt prosi się, aby dla templates utworzyć osobny katalog, więc tak też zrobiłem.
Wydaje się, że teraz jest Git, chociaż zamiast MVC i katalogów w application/ Ciontroller, Model, View mam teraz Controller, Model i ../templates/ .

Tu pojawia się kolejne pytanie, np. w IndexController miałem linie
$this->view("blog/index.html.php", $data); gdzie template był w index.html.php - tak to jakoś wymyśliłem na początku, ale nie widziałem takich nazw plików
i tak teraz sobie przypomniałem, czy nie lepiej aby zamiast index.html.php był plik index.phtml i tak należałoby przerobić wszystkie pliki w katalogu templates?

Wracając do DI i więcej abstrakcji w projekcie.

Plik np. IndexController jak mam tutaj zrobić wstrzykiwanie przy konstruktorze i metodach:
public function __construct(DatabaseClass $database, TranslationClass $translation)
public function index(TranslationClass $translation, etc...)

Plik RoutClass:
linia 41: $controllerNamespace = 'App\\Controller\\' . $this->controller;
linia 42: $this->controller = new $controllerNamespace;

oraz model, plik FrameworkClass:
linia 76: return new $modelNamespace;

Jakiś pomysł jak poskładać to co jest "pod maską" w katalogu /library/dbmframework/ ?
Ponieważ z tego co widzę obecnie wyszedł dwusuw, a potrzebny jest czterosuw ekonomiczny 2.0 200KM smile.gif

Napisany przez: viking 29.01.2024, 11:42:03

Kontroler to generalnie nie jest miejsce na wciskanie całej logiki i wszystkich zależności. Gdybyś użył jakiegoś kontenera (jak podałem przykładowy php-di) to mógłbyś wtedy wykorzystać https://www.php-fig.org/psr/psr-11/ i np w jakimś swoim service używać $container->get(MyClass::class) żeby wyciągnąć instancję klasy. Trzeba by poczytać dokumentację bo ma kilka ciekawych rzeczy.

Napisany przez: Malinaa 2.02.2024, 22:57:21

Cytat(viking @ 29.01.2024, 11:42:03 ) *
jak podałem przykładowy php-di

Przeglądałem podane przykładowe PHP-DI, ale wymaga użycia Composera i wówczas pewnie można by zrobić coś na kształt Slim framework,
ale jak już pisałem powinno funkcjonować bez konieczności użycia Composera, który jest tu opcjonalny, jeśli projekt wymagałby większej rozbudowy itd.

Na potrzeby obecnego projektu "maluszka" mogłoby wystarczyć to co jest, ale potrzebuję zastosować w kodzie DI (wstrzykiwanie) tylko bez żadnych gotowców,
gdyż bez tego o ile dobrze widzę jakakolwiek nieco większa rozbudowa nie ma sensu, ponieważ idąc już parę kroków dalej koło się zamyka, czyli lipa.

Dla przykładu zamiast obecnych templetów, które z rozszerzenia nazwa_pilku.html.php raczej przerobię na nazwa_pliku.phtml mogę chcieć rozbudować projekt, o któregoś gotowca (twig templating component),
zastosować np. Slim\Views\Twig chyba można ? i już tutaj muszę mieć w kontrolerach wstrzykiwanie. Jednak należy mieć na względzie, że to tylko opcja (jeżeli zdecyduje się użyć Composera, domyślnie templates/ będą z rozszerzeniem .phtml).

P.S. Sorki, ale obecnie nie miałem kiedy do tego usiąść, może przy weekendzie coś wymyślę.

Napisany przez: viking 3.02.2024, 15:34:29

Musisz się w końcu zdecydować. Robisz to żeby się coś nauczyć i ewentualnie szukać potem pracy jako programista czy robisz coś dla siebie i nie ma znaczenia jak, byle jaki kod który jakoś działa. Nie oszukujmy się że ktoś ten "framework" prywatnie wykorzysta więc raczej chodzi o naukę. A z takim podejściem sam sobie kłody pod nogi rzucasz.

Napisany przez: Malinaa 3.02.2024, 21:54:10

Cytat(viking @ 3.02.2024, 15:34:29 ) *
Musisz się w końcu zdecydować.

Jestem zdecydowany i nie interesuje mnie byle jakość. Tylko nie za bardzo rozumiem czy myślisz, że naprowadzasz mnie na właściwą drogę kiedy piszę któryś raz, że chcę jak najbardziej czysty PHP i HTML w templates, bez gotowców,
a opcjonalnie mieć możliwość użycia Composera i gotowych rozwiązań. Podałeś wcześniej "rozwiązania", które chyba nie rozwiązały żadnego opisanego problemu
i przy pierwszej lepszej okazji wytykasz swoje mądrości pisząc jakby to była jedyna dobra droga, oszukując jednocześnie na temat byle jakości, kiedy chyba nawet sam "czujesz", że nie po to tu jestem.

Pewnie jak zwykle i tak się zrozumiemy umiarkowanie, ale chciałbym zrobić to wstrzykiwanie np. w kontrolerach bez konieczności użycia Composera. Co w tym złego?
Napisałem to samo już któryś raz, ma się rozumieć nie da się bez Composera (plus PSR) itd. zrobić bezbłędnie, więc po temacie.

Z punktu widzenia rzeczy, o których się fizjologom nie śniło, tylko blondynom - jak to jest w WordPress CMS - użytkownik pobiera aplikacje, instaluje i gotowe, nie ma konieczności, ale zapewne można używać Composera?
Może przypasowałby tu silnik frameworka, a funkcjonalność CMS -> "Framework CMS", czy można jedno z drugim pogodzić, pewnie tak, ale wyjdzie "rakieta".

Napisany przez: viking 4.02.2024, 06:53:24

Wstrzykiwanie zależności a menedżer (albo service locator w zależności od tego co chcemy użyć) to dwie różne rzeczy. Do samego di nie potrzebujesz composer.
Widok można sobie wstawić dowolny, jeśli będzie jeden standard to kod powinien implentowac jeden wspólny interfejs i wymiana to przełączenie w konfiguracji. To czy twój szablon będzie miał rozszerzenie phtml czy zupa to najmniejszy problem.
Ciężko się z tobą rozmawia, bo nie tylko ja zwracam uwagę na pewne rzeczy, które następnie ignorujesz, bo masz swoją lepszą wizję. Tylko niestety jesteś na bardzo początkującym poziomie i widać to po tych pomysłach. Skup się na tym żeby pisać dobry kod a nie archaizmy typu brak composer. Tak się pisało 15 lat temu. Tak, możesz się teraz na mnie obrazić ale mało mnie to interesuje. Cześć.

Napisany przez: Malinaa 4.02.2024, 10:05:54

Kilka konkretnych informacji się pojawiło. Dzięki.

Na tym można by poprzestać, ale jak zwykle musiałeś pojechać z poziomem... No, jesteś Najlepszy.

P.S. Oj, Królu Złoty sądzisz, że z Tobą się lekko rozmawia, nie obrażajmy się,
ewentualnie jeśli kogoś szukasz, wybacz proszę nie jestem ideałem.

Composer jest zajebisty, więc nie obracaj kota ogonem, nigdzie nie pisałem że chcę bez Composera.
Wyobraź sobie wymóg jest taki, że musi działać też bez niego.

Napisany przez: nospor 4.02.2024, 20:07:12

skoro tak bardzo nie chcesz uzywac DI co ci zaproponowano, to :

- modele niech nie dziedziczac po DatabaseClass tylko niech przyjmuja w konstuktorze obiekt DatabaseClass i go uzywaja
- obiekt DatabaseClass tworzysz raz w index.php i potem przekazujesz w konstruktorach dalej.
- np do FrameworkClass, ktore potem w metodzie model() nie przekazuje obiekt DatabaseClass do konkretnych modeli.

I tyle, zmian malo a masz pelne DI.

ps: i miejze litosc wywal te "Class" z nazw klas w koncu. To mialo sens 20 lat temu gdy klasy dopiero wchodzily a nie teraz gdy wszystko jest klasa

Napisany przez: Malinaa 5.02.2024, 22:59:20

Cytat(nospor @ 4.02.2024, 20:07:12 ) *
1. modele niech nie dziedziczac po DatabaseClass tylko niech przyjmuja w konstuktorze obiekt DatabaseClass i go uzywaja
2. obiekt DatabaseClass tworzysz raz w index.php i potem przekazujesz w konstruktorach dalej.
3. np do FrameworkClass, ktore potem w metodzie model() nie przekazuje obiekt DatabaseClass do konkretnych modeli.


Zacząłem od początku od pliku index.php tu nie spodobało mi się poprzednie:

### RENDER PAGE ###
use Dbm\Classes\RoutClass;
new RoutClass();

Sprawdziłem co tam kiedyś napisałem w wersji 1 i nie maiłem routes, tylko taką sprytna metodę.
Dopisuję routing, ale po drodze stwierdziłem, że całość jest do przerobienia: struktuira katalogów i po kolei, przy okazji zmieniłem te Class na bez Class, bo tak chciałeś (mi to nie przeszkadzało, wydawało się, że podobnie jak NameInterface tak też NameClass to nie problem, a chociaż od razu widać, że to np. nie AbstractClass, czy coś jeszcze innego wink.gif mniejsza o to teraz jest bez i jest OK).

Teraz zamiast powyższego w pliku index.php mam
### ROUTER ###
$routes = require(BASE_DIRECTORY . 'application' . DS . 'routes.php');
$routes();
Pytanie, czy tak jest dobrze?

1. modele niech nie dziedziczac po DatabaseClass tylko niech przyjmuja w konstuktorze obiekt DatabaseClass i go uzywaja
Wcześniej bez zmiany na $routes nie można było nic do konstruktora w modelach wstrzyknąć po dodaniu kodu __construct() wysypywało się.
2. jak z pliku index.php przekazać Database w konstruktorach?
3. zamiast FrameworkClass jest AbstractController, - zmieniłem tu stary pomysł na modele bez możliwości używania konstruktora - teraz można wstrzykiwać w modelach.

Mamy plik index.php (w katalogu public) w przeglądarce otwieram stronę główną,
obsługuje ją klasa IndexController (src/Controller/) tu nadal coś jest nie tak, nie mogę wstrzyknąć do konstruktora

Kod
/*
     * TODO! public function __construct(Database $database, Translation $translation) // TODO! Wstrzykiwanie do konstruktora!?
     */
    public function __construct()
    {
        $database = new Database;

        $model = new BlogModel($database);
        $this->model = $model;

        $translation = new Translation();
        $this->translation = $translation;
    }


w powyższym konstruktorze jest model, no udało się tu poukładać kod i wstrzykuje

Plik BlogModel (src/Model)

Kod
public function __construct(Database $database)
    {
        $this->database = $database;
    }


Czy tak jest ok, bo coś nie czuje jak to jest z tym połączeniem, aby mieć jedno?

Pozostał przykładowy IndexController wstrzykiwanie do konstruktora, a do metod, czy też?
No i dalej abstract klasy, interfejsy, fabryki, I tyle, więcej nie pamiętam.

Zaproponowane DI będę chciał się zająć, ale w pierwszej kolejności chciałbym dobrze poskładać całość bez Composera i gotowców.

Napisany przez: nospor 6.02.2024, 09:31:42

Niby sluchasz co sie do ciebie mowi ale jednoczesnie nie sluchasz.

Mowie wyraznie: obiekt Database ma robic w index.php a nie w zadnym indexController....

Obiekt $database, ktory zrobisz w index.php masz potem przekazac do wszystkiego innego co go uzywa. A skoro z index.php przechodzisz do Route, to masz ten obiekt $database przekazac do Route a potem w route przekazujesz dalej czyli do kontrolerow itd itd. Schodzisz caly czas w dol, tu nie ma zadnej magii

Napisany przez: Malinaa 6.02.2024, 10:37:49

Jeżeli słucham to jak mogę nie słuchać. Rozumiem, że nie ma w tym żadnej magii, ale nie potrafię zrobić tego przejścia dobrze, coś się wysypuje i w tym problem.
Piszemy o tym któryś raz i jak piszesz dla Ciebie to proste, a nie kumam, nie że nie słucham, a zawiesiłem się w tym miejscu (chyba potrzebny reset ;-)

Database mam zrobić w public/index.php, czyli jak rozumiem ma być tak:

// Routing and database connection
use Dbm\Classes\Database;

$database = new Database;

$routes = require(BASE_DIRECTORY . 'application' . DS . 'routes.php');
$routes($database); // -> dodane $database

dalej mam plik routes.php

return function (Database $database) { // -> dodane $database
$uri = $_SERVER['REQUEST_URI'];

$router = new Router();
...
}

następnie to nic mi z tego przekazywania nie wychodzi, więc jak dalej... ?

Napisany przez: nospor 6.02.2024, 11:14:14

No masz dalej dodawac w dol te $database

$router = new Router($database);


Cytat
Jeżeli słucham to jak mogę nie słuchać.

Skoro mowie index.php a ty dodajesz w INdexController to jak ja mam to rozumiec skolei?

Napisany przez: Malinaa 6.02.2024, 11:48:20

Dotąd już miałem
$router = new Router($database); i więcej..
ale idąc dalej i słuchając i tak musiałem dodać w IndexController bo się sypało,
w którymś miejscu dalej nie wychodzi

Napisany przez: nospor 6.02.2024, 12:14:57

zajebisty opis problemu: bo sie sypalo.. Jak masz problem z kodem ktory robimy, to podajesz komunikat bledu a nie walisz tekstem "bo sie sypalo" i totalnie zmieniasz koncepcje. No tak do niczego nie dojdziemy

Przeciez w Router masz

$controller = new $controller();

no to jaki problem zrobic z tego
$controller = new $controller($database);

?
oczywiscie konstruktor kontrolera tez ma przyjmowac ten parametr.

Napisany przez: Malinaa 6.02.2024, 15:18:56

Dobrze, że pokazałeś palcem, bo nie mogłem tego znaleźć,
sorki zamotałem się, a tak samo proste jak w index.php,
ale przy zaktualizowanym kodzie w index.php jednak nie dajemy, tylko w klasie Router ? :

$database = new Database;
$controllerInstance = new $controller($database);

po czym w AbstractController

public function __construct(Database $database)
{
$this->database = $database;
}

i teraz jest ok, bo działa w IndexController:

Kod
/*
     * TODO! public function __construct(Database $database, Translation $translation) // TODO! Wstrzykiwanie do konstruktora!?
     */
    public function __construct(Database $database)
    {
        $model = new BlogModel($database);
        $this->model = $model;

        $translation = new Translation();
        $this->translation = $translation;
    }

    /*
     * TODO! public function index(Translation $translation, etc...) // TODO! Wstrzykiwanie do metody!?
     *
     * @Route: "/"
     */
    public function index()
    {
    ...
    }


i modele wygląda że są już super - konstruktor śmiga i mamy jedno połączenie.

Pozostały jeszcze zapytania o IndexController
1. czy można / jak zrobić aby wstrzykiwać do woli co tylko potrzebne
public function __construct(Database $database, Translation $translation, FirstService, SecondService, Three, etc....)
2. oraz wstrzykiwanie do metody ? np.
public function index(Translation $translation, etc...)

Napisany przez: nospor 6.02.2024, 15:26:07

Cytat
ale przy zaktualizowanym kodzie w index.php jednak nie dajemy, tylko w klasie Router ? :
No i widzisz aty dalej swoje. Ja ci mowie jedno, a ty i tak robisz po swojemy.
Wyrnaznie pisalem ze to ma byc w index.php.
Teraz $database stworzyles w Router, na dodatek warunkowo przed tworzeniem kontrollera. Ale przeciez $database moze byc wykorzystawane w innych miejscach a nie tylko w kontrolerze a ty to wlasnie ograniczyles do kontrolera.

Cytat
1. czy można / jak zrobić aby wstrzykiwać do woli co tylko potrzebne
public function __construct(Database $database, Translation $translation, FirstService, SecondService, Three, etc....)

Albo powtarzasz kroki z database, albo wkoncu sciagasz DI co ci proponowano.

Napisany przez: Malinaa 6.02.2024, 16:22:25

No, aż mi głupio, ale jeszcze nie mogę uwierzyć, że aby było ok trzeba wykonać aż takie przejście z pliku do pliku i plik po pliku.

Jest tak w pliku index.php

// Routing and database connection
use Dbm\Classes\Database;
$database = new Database;
$routes = require(BASE_DIRECTORY . 'application' . DS . 'routes.php');
$routes($database);

routes.php

return function (Database $database) {
$uri = $_SERVER['REQUEST_URI'];
$router = new Router($database);
...
}

class Router

public function __construct(Database $database)
{
$this->database = $database;
}

if (class_exists($controller)) {
$controllerInstance = new $controller($database);
...
}

AbstractController - nie jest abstract, więc nazwa do zmiany

public function __construct(Database $database)
{
$this->database = $database;
}

IndexController

public function __construct(Database $database)
{
$model = new BlogModel($database);
$this->model = $model;

$translation = new Translation();
$this->translation = $translation;
}

aż dochodzę do modelu BlogModel

public function __construct(Database $database)
{
$this->database = $database;
}

Czy teraz jest Ok?

Napisany przez: nospor 6.02.2024, 16:29:49

nie
aż dochodzę do modelu BlogModel

public function __construct(Database $database)
{
$this->database = $database;
}

a:

aż dochodzę do modelu BlogModel

public function __construct(Database $database)
{
parent::__construct($database);
// inne rzeczy w konstruktorze BlogModel
}



Moze to i glupio wyglada ale tak sie robi. W sensie nie robi sie tak jak ty to robisz, tylko sie uzywa automatow co juz ci proponowano. Ale tak wlasnie generalnie wyglada DI. Obiekt jest tworzony raz a potem przekazywany wszedzie tam gdzie potrzeba. Ulatwi to wiele spraw, chocby testowanie aplikacji

Napisany przez: Malinaa 6.02.2024, 16:57:05

Po zmianie w __construct()

Message: Cannot use "parent" when current class scope has no parent
File: Model\BlogModel.php on line 21

Napisany przez: nospor 6.02.2024, 17:22:18

sorki, mialem na mysli kontrollery a nie model.

czyli nie
IndexController

public function __construct(Database $database)
{
$model = new BlogModel($database);
$this->model = $model;

$translation = new Translation();
$this->translation = $translation;
}

a
IndexController

public function __construct(Database $database)
{
parent::__construct($database);
$model = new BlogModel($database);
$this->model = $model;

$translation = new Translation();
$this->translation = $translation;
}

Napisany przez: Malinaa 6.02.2024, 18:50:18

Aha, czyli dodać tylko linijkę:
parent::__construct($database);
w konstruktorze, ponieważ dziedziczy z kontrolera bazowego,
no i już wiem jak nazwać kontroler bazowy
AbstractController to BaseController
no i wszystko jasne smile.gif

Kod
Moze to i glupio wyglada ale tak sie robi. W sensie nie robi sie tak jak ty to robisz, tylko sie uzywa automatow co juz ci proponowano. Ale tak wlasnie generalnie wyglada DI. Obiekt jest tworzony raz a potem przekazywany wszedzie tam gdzie potrzeba. Ulatwi to wiele spraw, chocby testowanie aplikacji


A to nawet nie jest bez sensu zrobić takie ćwiczenie wink.gif

Ok, po zmianie został jeszcze routing, takie sobie linki SEO friendly wymyśliłem, np.:
/praesent-euismod-gravida-libero-a-luctus-nisi-fermentum-nec,art,5.html
że nie ma lekko z tym routingiem.

Mniejsza z tym routingiem, czy jest tu jakieś miejsce gdzie przydałby się np. AbstractController, albo Interfejs, a może Fabryczka ?; )

Napisany przez: nospor 7.02.2024, 10:47:34

Cytat
Ok, po zmianie został jeszcze routing, takie sobie linki SEO friendly wymyśliłem, np.:
/praesent-euismod-gravida-libero-a-luctus-nisi-fermentum-nec,art,5.html
że nie ma lekko z tym routingiem.

No ale jakies pytanie w zwiazku z tym czy tylko komunikujesz? wink.gif

Cytat
Mniejsza z tym routingiem, czy jest tu jakieś miejsce gdzie przydałby się np. AbstractController, albo Interfejs, a może Fabryczka ?; )

No interfejsy daje sie w zasadzie wszedzie. u ciebie napewno na wszystko to co wstrzykujesz powinny byc interfejsy

Napisany przez: com 7.02.2024, 15:09:08

Cytat(Tomplus @ 24.01.2024, 09:31:42 ) *
Ja osobiście rozumiem dlaczego ktoś tworzy i stosuje własne FW. Mają kilka zalet:
- na początku są proste w obsłudze
- są lekkie
- uwzględniają specyfikę hostingu
- są szyte na miarę umiejętności twórcy.

Jednak problemy przychodzą gdy, projekt się rozrasta i różnych powiązań modułów jest coraz więcej. Zaczynają pojawiać się zbędne powtórzenia, blędy tam gdzie nie powinno ich być.


No i to jest pierwszy i podstawowy błąd, kiedyś kiedy środowisko PHP raczkowało powstawało wiele potworków, która były systemami Legancy już na samym starcie projektu, potem projekt coraz bardziej się rozrastał i zainwestowanych środków nikt już nikomu nie zwróci, wiec utrzymuje się takie twory po dziś, ale w 2024 roku powinno nie być już zrozumienia dla takich praktyk. Sam tak jak pisałem wyżej wiele lat temu tworzyłem własne bo mi się jak każdemu wydawało że prosty projekt to poco zatrudniać taka kobyle jak pełnoprawny FW, ale czasy mamy inne i FW też pozwalają na to by dla mikroporjektów odpalać je i nie jest to taki już ogromny narzut jak kiedyś, dlatego uśmiercono już Silexa czy Lumena, bo Symfony czy Laravel potrafią je w pełni zastąpić. A największym problemem tego było to co napisałeś: "są szyte na miarę umiejętności twórcy.", w zeszył roku przez pewien czas przyszło mi pracować z takim systemem, jego poziom rozwoju zatrzymał się na PHP 5.4, a na starcie wszystkie błędy musiały być tam filtrowane bo było ponad 500 błędów o tym, że używana jest metoda klasy jako statyczna metoda, nie było szans na podbicie nawet do 5.5, bez przepisania połowy tego kodu, pomijając to że jest tak napisany ze nawet rector by sobie z tym nie poradził, w planach miało być przejście na PHP 5.6 a potem 7, ale jak wiadomo nowe funkcjonalności wygrały z naprawa tego co przez lata tam napsuto. Dlatego nie róbmy sobie krzywdy tworząc takie fw dla ideii, jeśli chcemy poznać jak coś działa to kod jest open source można przejrzeć źródła i testy i nawet jak nadal uparcie chcemy coś pisać to trzeba już mieć doświadczenie tka jak ktoś już tutaj wcześniej o tym wspominał.

Cytat
Malinaa musisz sobie postawić zasadnicze pytanie poco to tworzysz?


Cytat(Malinaa @ 18.01.2024, 21:38:13 ) *
Pan Bóg stworzył istotę dla hecy, z tyłu plecy, z przodu plecy. A jednak po niej przyszedł ten, który był zrodzony, a nie stworzony smile.gif
Chcesz pomóc? Napisz jak mam zakodować DI i inne drobiazgi, o których pisałem wyżej.


Chcę pomoc ale kompletnie nie zrozumiałeś przekazu, wysłałem Ci wartościowy materiał https://symfony.com/doc/current/create_framework/index.html do którego nie zajrzałeś nawet. Uparłeś się żeby stworzyć FW bez composera, co w 2024 jest już niedopuszczalne w żadnym greenfield`zie. Kiedy w końcu PHP dojrzało do pewnych standardów które w Javie, czy C# mamy od lat i za których brak PHP przez lata był hejtowany, ty robisz wszytko by nadal powielać wszystkie złe praktyki jakie przez lata w tym języku stosowano. Wysiałem tobie również https://phptherightway.com/ tutaj również nie zajrzałeś, tam masz odpowiedzi chociażby na pytanie jak wdrożyć DI https://phptherightway.com/#dependency_injection Skoro chcesz poznać jak to działa to o tym poczytaj, nie oczekuj że podam Ci coś na tacy bo wtedy nie poznasz jak to działa, ale jak chcesz naprawdę czegoś się nauczyć z tworzenia tego FW to zanim zaczniesz pisać kod musisz zglebić te zagadnienia i przed wszystkim słuchać rad innych, którzy też kiedyś to poznawali tak jak Ty, nikt się nie budzi z tą wiedzą, zobacz przez ile postów z nospor pisałeś o tym jak wdrożyć DI, a wynikało to z tego że nie wiedziałeś czym jest ten wzorzec, wiec tego nie potrafiłeś przełożyć na kod. Jeśli chcesz naprawdę do czegoś w tym FW dojść to musisz bardzo dużo poczytać o tym, a dopiero potem próbować to przełożyć na kod, my siadamy do kodu bo już znamy pojecie DI itp, ty je poznajesz wiec to normalne że nie widzisz tego jeszcze w kodzie, ale jakbyś miał już teorie za sobą to byś to tak samo widział jak my smile.gif Patrzysz na to że Wordpress nie ma composera, co oczywiście jest prawdą ale on powstał w 2004 roku, tam ten kod jest tak stary że jak powstawał było jeszcze PHP 4.X, potem 5.0, stąd ma wiele rzeczy których by w 2024 roku nie zastosowano, zaczynając od konwencji nazewnictwa klas i metod, braku composera w oficjalnym core i wsparcia dla PSR, ale ten kod ma 20 lat, nikt czegoś co przez 20 lat szczególnie w ramach open source nie przepisze na obecne standardy, będzie to utrzymywane w tej formie do końca jego istnienia, ale przez to własnie PHP jest tak bardzo nienawidzony, za Wordpressa bo nadal ludzie myślą że PHP wygląda tak jak Wordpress, ale jak ten system blogowy zasila jakieś 60% internetu to nie ma możliwości tak drastycznych zmian, bo tam musi być zachowana kompatybilność by 60% internetu się nie wysypało, ale to nie znaczy, że jesteśmy zmuszeni do tego by dalej tak pisać w Wordpressie, bo nie jesteśmy możemy użyć https://roots.io/bedrock/ i mieć kod na miarę 2024 roku i musisz to kolego zrozumieć, że nie jesteśmy już w 2004 roku, nie ma miejsca na tworzenie tego w ten sposób jeśli faktycznie chcesz się czegoś nauczyć i kiedyś nam za to podziękujesz że nie szedłeś tą ślepą drogą smile.gif Jak zaczniesz pisać to tak jak na 2024 rok przystało wtedy bardzo chętnie zrobię Ci Review kodu smile.gif Swoją drogą już Ci pisałem MVC to tylko pewna konwencja(https://www.youtube.com/watch?v=pJSoS4VBIW4&themeRefresh=1 poza tym obejrzyj sobie to), nie da się jej zaadaptować do WEB, to zawsze będzie jakaś wariacja wokół tego i nie ma problemu by katalog z templatkami nazywał się templates, a jak robisz templatki w czystym PHP/HTML to jak najbardziej dobrym rozszerzeniem dla tego jest phtml, to już taki standard(wprawdzie już raczej się nie używa tylko systemów jak twig ale w teorii można)

  1. Fatal error: Uncaught TypeError: Argument 1 passed to Dbm\Classes\Router::matchRoute() must be of the type string, null given, called in \www\dbm\application\classes\Router.php on line 38 and http://www.php.net/defined in \www\dbm\application\classes\Router.php:83 Stack trace: #0 \www\dbm\application\classes\Router.php(38): Dbm\Classes\Router->matchRoute(NULL) #1 \www\dbm\application\routes.php(43): Dbm\Classes\Router->dispatch(NULL) #2 \www\dbm\public\index.php(54): {closure}(Object(Dbm\Classes\Database)) #3 {main} thrown in \www\dbm\application\classes\Router.php on line 83


Swoją droga nie można tego nawet uruchomić bo na sztywno zaszyłeś w metodzie dispatch w 37 lini klasy Router
  1. $uri = $this->matchLocalhost($uri);

i nie można było tego odpalić na jakiś vhoscie, to powinno być opcjonalne albo konfigurowalne. Fajne logo takie trochę kradzione od JetBrains wink.gif

Cytat(Malinaa @ 6.02.2024, 18:50:18 ) *
Ok, po zmianie został jeszcze routing, takie sobie linki SEO friendly wymyśliłem, np.:
/praesent-euismod-gravida-libero-a-luctus-nisi-fermentum-nec,art,5.html
że nie ma lekko z tym routingiem.

To nie jest akurat SEO friendly https://www.seroundtable.com/google-commas-brackets-faceted-navigation-32741.html smile.gif

Prosze tutaj obsługa routingu(żeby nie było że nie chcę pomoc)
Klasa Router
  1.  
  2. class Router
  3. {
  4. protected $routes = [];
  5. private $database;
  6.  
  7. public function __construct(Database $database)
  8. {
  9. $this->database = $database;
  10. }
  11.  
  12. public function addRoute(string $route, http://www.php.net/array $arrayController): void
  13. {
  14. $arrayControllerAction = $this->changeArrayKey($arrayController, ['controller', 'method']);
  15. $this->routes[$route] = $arrayControllerAction;
  16. }
  17.  
  18. public function dispatch(string $uri): void
  19. {
  20. $database = $this->database;
  21.  
  22. $uri = $this->matchLocalhost($uri);
  23. $route = $this->matchRoute($uri);
  24. $uri = $route['uri'];
  25.  
  26. $hasParams = false;
  27. if (!http://www.php.net/empty($route['params'])) {
  28. $hasParams = true;
  29. $uri = $this->buildRouteUri($route['paths'], $route['params']);
  30. }
  31.  
  32. if (http://www.php.net/array_key_exists($uri, $this->routes)) {
  33. $controller = $this->routes[$uri]['controller'];
  34. $method = $this->routes[$uri]['method'];
  35.  
  36. if (class_exists($controller)) {
  37. $controllerInstance = new $controller($database);
  38.  
  39. if (method_exists($controllerInstance, $method)) {
  40. if ($hasParams) {
  41. $controllerInstance->$method((int)http://www.php.net/end($route['params']));
  42. } else {
  43. $controllerInstance->$method();
  44. }
  45.  
  46. } else {
  47. throw new ExceptionHandler("No method $method on class $controller!", 500);
  48. }
  49. } else {
  50. throw new ExceptionHandler("No controller $controller!", 500);
  51. }
  52. } else {
  53. throw new ExceptionHandler("Route not found! addRoute('$uri')", 404);
  54. }
  55. }
  56.  
  57. private function changeArrayKey(http://www.php.net/array $array, http://www.php.net/array $keys): http://www.php.net/array
  58. {
  59. foreach ($array as $key => $value) {
  60. $newArray[$keys[$key]] = $value;
  61. }
  62.  
  63. return $newArray;
  64. }
  65.  
  66. private function matchLocalhost(string $uri): ?string
  67. {
  68. $haystack = APP_PATH;
  69. $needle = 'localhost';
  70.  
  71. if (http://www.php.net/strpos($haystack, $needle) !== false) {
  72. $folder = http://www.php.net/substr($haystack, http://www.php.net/strpos($haystack, $needle) + http://www.php.net/strlen($needle));
  73.  
  74. return http://www.php.net/str_replace($folder, '/', $uri);
  75. }
  76.  
  77. return null;
  78. }
  79.  
  80. private function buildRouteUri(http://www.php.net/array $paths, http://www.php.net/array $params): string
  81. {
  82. $params[0] = '{#}';
  83. $params[http://www.php.net/count($params) - 1] = '{id}';
  84. return '/'.http://www.php.net/implode('/', $paths). '/'. http://www.php.net/implode(',', $params).'.html';
  85. }
  86.  
  87. private function matchRoute(string $uri): http://www.php.net/array
  88. {
  89. $path = filter_var($uri, FILTER_SANITIZE_URL);
  90. $path = http://www.php.net/ltrim($path, '/');
  91. $path = http://www.php.net/explode("/", $path);
  92.  
  93. if (($pos = http://www.php.net/strpos($uri, '?')) !== false) {
  94. $uri = http://www.php.net/substr($uri, 0, $pos);
  95. }
  96. $paths = [];
  97. $params = [];
  98. foreach ($path as $subPath) {
  99. if (http://www.php.net/strpos($subPath, '.html') !== false) {
  100. $params = http://www.php.net/explode(',', $subPath);
  101. $param = http://www.php.net/end($params);
  102. if (($pos = http://www.php.net/strpos($param, '.html')) !== false) {
  103. $params[http://www.php.net/count($params) - 1] = http://www.php.net/substr($param, 0, $pos);
  104. }
  105. } else {
  106. $paths[] = $subPath;
  107. }
  108.  
  109. }
  110.  
  111. /*if (strpos($uri, ',') !== false) {
  112.   $uri = substr($uri, 0, 1) . substr($uri, strpos($uri, ',') + 1);
  113.   }*/
  114.  
  115. /* $parts = explode(',', $uri);
  116.   $uri = '/' . array_pop($parts); */
  117.  
  118. foreach($path as $index => $param){
  119. if(http://www.php.net/preg_match("/{.*}/", $param)){
  120. $indexNum[] = $index;
  121. }
  122. }
  123.  
  124. //print_r($indexNum);
  125.  
  126. /*if ((strpos($uri, '-')) !== false) {
  127.   //$parts = explode('/', $uri);
  128.   //$last = array_pop($parts);
  129.   //echo ' | '. $last;
  130.  
  131.   $link = str_replace(['/', '.html'], '', $uri);
  132.   $segments = explode(',', str_replace('/', '', $link));
  133.  
  134.   foreach ($segments as $key => $value) {
  135.   if (is_numeric($value)) {
  136.   $segments[$key] = '{$}';
  137.   }
  138.   if ((strpos($value, '-')) !== false) {
  139.   $segments[$key] = '{#}';
  140.   }
  141.   }
  142.  
  143.   //$uri = '/' . $path[0] . '/' . implode(',', $segments) . '.html';
  144.   $uri = '/' . implode(',', $segments) . '.html';
  145.  
  146.   //echo $uri;
  147.   }*/
  148.  
  149. return [
  150. 'uri' => $uri,
  151. 'paths' => $paths,
  152. 'params' => $params
  153. ];
  154. }
  155.  
  156. /* private function requestMethod() : string
  157.   {
  158.   return $_SERVER['REQUEST_METHOD'];
  159.   }
  160.  
  161.   private function requestPath(): string
  162.   {
  163.   return parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
  164.   } */
  165. }
  166.  


Plik routes.php

  1. return function (Database $database) {
  2. $uri = $_SERVER['REQUEST_URI'];
  3.  
  4. $router = new Router($database);
  5.  
  6. $router->addRoute('/', [IndexController::class, 'index']);
  7. $router->addRoute('/link.html', [IndexController::class, 'linkMethod']);
  8. $router->addRoute('/home.html', [HomeController::class, 'index']);
  9. $router->addRoute('/about.html', [AboutController::class, 'index']);
  10. $router->addRoute('/contact.html', [ContactController::class, 'index']);
  11. $router->addRoute('/regulation.html', [RegulationController::class, 'index']);
  12. $router->addRoute('/page', [PageController::class, 'index']);
  13. $router->addRoute('/page/site', [PageController::class, 'siteMethod']); // ? /page/site.html
  14. $router->addRoute('/site.html', [PageController::class, 'siteMethod']); // ?
  15. $router->addRoute('/offer.html', [PageController::class, 'offerMethod']); // /{#},offer.html
  16. $router->addRoute('/blog', [BlogController::class, 'index']);
  17. $router->addRoute('/blog/sections', [BlogController::class, 'sectionsMethod']);
  18. $router->addRoute('/blog/{#},sec,{id}.html', [BlogController::class, 'sectionMethod']);
  19. $router->addRoute('/blog/{#},art,{id}.html', [BlogController::class, 'articleMethod']);
  20.  
  21. $router->dispatch($uri);
  22. };



W pliku routes musiałem poprawić route dla artykułow bo pattern się nie zgadzał z regułami rewrite oraz linkami na stronie

  1. private function buildRouteUri(http://www.php.net/array $paths, http://www.php.net/array $params): string
  2. {
  3. $params[0] = '{#}';
  4. if (http://www.php.net/is_numeric($params[http://www.php.net/count($params) - 1])) {
  5. $params[http://www.php.net/count($params) - 1] = '{id}';
  6. }
  7. if (!http://www.php.net/empty($paths)) {
  8. $paths = '/'.http://www.php.net/implode('/', $paths). '/';
  9. } else {
  10. $paths = '/';
  11. }
  12. return $paths . http://www.php.net/implode(',', $params).'.html';
  13. }


Mała zamiana do obsługi stron typu the-best-offer,offer.html

  1. private function matchRoute(string $uri): http://www.php.net/array
  2. {
  3. $path = filter_var($uri, FILTER_SANITIZE_URL);
  4. $path = http://www.php.net/ltrim($path, '/');
  5. $path = http://www.php.net/explode("/", $path);
  6.  
  7. if (($pos = http://www.php.net/strpos($uri, '?')) !== false) {
  8. $uri = http://www.php.net/substr($uri, 0, $pos);
  9. }
  10. $paths = [];
  11. $params = [];
  12. foreach ($path as $subPath) {
  13. if (http://www.php.net/strpos($subPath, '.html') !== false) {
  14. $params = http://www.php.net/explode(',', $subPath);
  15. if (http://www.php.net/end($params) === $subPath) {
  16. $params = [];
  17. break;
  18. }
  19. $param = http://www.php.net/end($params);
  20. if (($pos = http://www.php.net/strpos($param, '.html')) !== false) {
  21. $params[http://www.php.net/count($params) - 1] = http://www.php.net/substr($param, 0, $pos);
  22. }
  23. } else {
  24. $paths[] = $subPath;
  25. }
  26.  
  27. }
  28.  
  29. /*if (strpos($uri, ',') !== false) {
  30.   $uri = substr($uri, 0, 1) . substr($uri, strpos($uri, ',') + 1);
  31.   }*/
  32.  
  33. /* $parts = explode(',', $uri);
  34.   $uri = '/' . array_pop($parts); */
  35.  
  36. foreach($path as $index => $param){
  37. if(http://www.php.net/preg_match("/{.*}/", $param)){
  38. $indexNum[] = $index;
  39. }
  40. }
  41.  
  42. //print_r($indexNum);
  43.  
  44. /*if ((strpos($uri, '-')) !== false) {
  45.   //$parts = explode('/', $uri);
  46.   //$last = array_pop($parts);
  47.   //echo ' | '. $last;
  48.  
  49.   $link = str_replace(['/', '.html'], '', $uri);
  50.   $segments = explode(',', str_replace('/', '', $link));
  51.  
  52.   foreach ($segments as $key => $value) {
  53.   if (is_numeric($value)) {
  54.   $segments[$key] = '{$}';
  55.   }
  56.   if ((strpos($value, '-')) !== false) {
  57.   $segments[$key] = '{#}';
  58.   }
  59.   }
  60.  
  61.   //$uri = '/' . $path[0] . '/' . implode(',', $segments) . '.html';
  62.   $uri = '/' . implode(',', $segments) . '.html';
  63.  
  64.   //echo $uri;
  65.   }*/
  66.  
  67. return [
  68. 'uri' => $uri,
  69. 'paths' => $paths,
  70. 'params' => $params
  71. ];
  72. }


oraz matchRoute by nie łapało niepotrzebnie /link.html jako pattern /{#}.html wtedy kiedy nie powinno wink.gif

poza tym powinno być raczej
1,user.html
a nie user,1.html skoro już taka konwencje przyjąłeś w offer smile.gif

ale jak tak ma być jak masz to wtedy jeszcze taka zmiana
  1. private function buildRouteUri(http://www.php.net/array $paths, http://www.php.net/array $params): string
  2. {
  3. if (http://www.php.net/count($params) > 2) {
  4. $params[0] = '{#}';
  5. }
  6. if (http://www.php.net/is_numeric($params[http://www.php.net/count($params) - 1])) {
  7. $params[http://www.php.net/count($params) - 1] = '{id}';
  8. }
  9. if (!http://www.php.net/empty($paths)) {
  10. $paths = '/'.http://www.php.net/implode('/', $paths). '/';
  11. } else {
  12. $paths = '/';
  13. }
  14. return $paths . http://www.php.net/implode(',', $params).'.html';
  15. }


i w routes.php

  1. return function (Database $database) {
  2. $uri = $_SERVER['REQUEST_URI'];
  3.  
  4. $router = new Router($database);
  5.  
  6. $router->addRoute('/', [IndexController::class, 'index']);
  7. $router->addRoute('/link.html', [IndexController::class, 'linkMethod']);
  8. $router->addRoute('/home.html', [HomeController::class, 'index']);
  9. $router->addRoute('/about.html', [AboutController::class, 'index']);
  10. $router->addRoute('/contact.html', [ContactController::class, 'index']);
  11. $router->addRoute('/regulation.html', [RegulationController::class, 'index']);
  12. $router->addRoute('/page', [PageController::class, 'index']);
  13. $router->addRoute('/page/site', [PageController::class, 'siteMethod']); // ? /page/site.html
  14. $router->addRoute('/site.html', [PageController::class, 'siteMethod']); // ?
  15. $router->addRoute('/offer.html', [PageController::class, 'offerMethod']); // /{#},offer.html
  16. $router->addRoute('/{#},offer.html', [PageController::class, 'offerMethod']); // /{#},offer.html
  17. $router->addRoute('/blog', [BlogController::class, 'index']);
  18. $router->addRoute('/blog/sections', [BlogController::class, 'sectionsMethod']);
  19. $router->addRoute('/blog/{#},sec,{id}.html', [BlogController::class, 'sectionMethod']);
  20. $router->addRoute('/blog/{#},art,{id}.html', [BlogController::class, 'articleMethod']);
  21. $router->addRoute('/{#},sec,{id}.html', [BlogController::class, 'sectionMethod']);
  22. $router->addRoute('/{#},art,{id}.html', [BlogController::class, 'articleMethod']);
  23. $router->addRoute('/user,{id}.html', [UserController::class, 'index']);
  24.  
  25. $router->dispatch($uri);
  26. };


Oczyścicie musisz poprawić UserController według nowej konwencji z DI

Popraw tez linki bo potem musisz mieć powielone reguły bo raz są z blog raz bez, zależy gdzie klikniesz:
  1. $router->addRoute('/blog/{#},sec,{id}.html', [BlogController::class, 'sectionMethod']);
  2. $router->addRoute('/blog/{#},art,{id}.html', [BlogController::class, 'articleMethod']);
  3. $router->addRoute('/{#},sec,{id}.html', [BlogController::class, 'sectionMethod']);
  4. $router->addRoute('/{#},art,{id}.html', [BlogController::class, 'articleMethod']);


Jeszcze małą zmiana by każdy pattern był obsługiwany
  1. private function buildRouteUri(http://www.php.net/array $paths, http://www.php.net/array $params): string
  2. {
  3. $paramsLength = http://www.php.net/count($params);
  4. if (!http://www.php.net/is_numeric($params[0]) && $paramsLength > 2) { // pattern /{#},sec,{id}.html itp
  5. $params[0] = '{#}';
  6. }
  7. if (!http://www.php.net/is_numeric($params[0]) && !http://www.php.net/is_numeric(http://www.php.net/end($params))) { // pattern /{#},offer.html
  8. $params[0] = '{#}';
  9. }
  10. if (http://www.php.net/is_numeric(http://www.php.net/end($params))) { // pattern /user,{id}.html
  11. $params[$paramsLength - 1] = '{id}';
  12. }
  13. if (!http://www.php.net/empty($paths)) {
  14. $paths = '/'.http://www.php.net/implode('/', $paths). '/';
  15. } else {
  16. $paths = '/';
  17. }
  18. return $paths . http://www.php.net/implode(',', $params) . '.html';
  19. }


oraz dispatch dla pn takiej reguły
  1. $router->addRoute('/your-website-title,site.html', [PageController::class, 'siteMethod']);

  1. public function dispatch(string $uri): void
  2. {
  3. $database = $this->database;
  4.  
  5. //$uri = $this->matchLocalhost($uri);
  6. $route = $this->matchRoute($uri);
  7. $uri = $route['uri'];
  8. $hasParams = false;
  9. if (!http://www.php.net/array_key_exists($uri, $this->routes)) {
  10. if (!http://www.php.net/empty($route['params'])) {
  11. $hasParams = true;
  12. $uri = $this->buildRouteUri($route['paths'], $route['params']);
  13. }
  14. }
  15.  
  16. if (http://www.php.net/array_key_exists($uri, $this->routes)) {
  17. $controller = $this->routes[$uri]['controller'];
  18. $method = $this->routes[$uri]['method'];
  19.  
  20. if (class_exists($controller)) {
  21. $controllerInstance = new $controller($database);
  22.  
  23. if (method_exists($controllerInstance, $method)) {
  24. if ($hasParams) {
  25. $controllerInstance->$method((int)http://www.php.net/end($route['params']));
  26. } else {
  27. $controllerInstance->$method();
  28. }
  29.  
  30. } else {
  31. throw new ExceptionHandler("No method $method on class $controller!", 500);
  32. }
  33. } else {
  34. throw new ExceptionHandler("No controller $controller!", 500);
  35. }
  36. } else {
  37. throw new ExceptionHandler("Route not found! addRoute('$uri')", 404);
  38. }
  39. }

Napisany przez: Malinaa 8.02.2024, 11:44:53

Cytat(nospor @ 7.02.2024, 10:47:34 ) *
No ale jakies pytanie w zwiazku z tym czy tylko komunikujesz? wink.gif
No interfejsy daje sie w zasadzie wszedzie. u ciebie napewno na wszystko to co wstrzykujesz powinny byc interfejsy


Routing jest w trakcie budowy. Pierwsza wersja działa tylko na localhost po konfiguracji w kilku prostych krokach wg. instrukcji w README,
ale kiedy utworzyłem na Windows domenę dla testów okazuje się, że w domenie routing nie działa, więc muszę poprawić.

Są tu pozostałości z wersji pierwszej FW, np. /public/ .htaccess
RewriteRule ^(.+)$ index.php?url=$1 [QSA,L] tego ?url=$1 nie powinno już być (było potrzebne przy sprytnej metodzie zamiast routingu)!?
Trzeba by pliki .htaccess w /public/ i katalogu głównym zrobić do porządku, aby routing działał na serwerze zdalnym i lokalnym.

Pozbyłem się sprytnej metody (która funkcjonowała jako routing na local i zdalnym) i utworzyłem na jej miejsce routing, który coś nie bardzo chce zadziałać na zdalnym.
Jeżeli przychodzi pomysł jak poprawić wymienione elementy, prośba o info.

Usuwając 'url' będą też inne metody do poprawienia, np. class DataFlatfile -> function fileName()
poza tym przy routingu wykorzystywany był define('APP_PATH', ''); co tez nie jest za dobrze, APP_PATH to raczej miało być używane dla szablonów mailera itp.
register-created-account.html -> {page_address} ale nie w routingu.

Interfejsy. No to od czego zaczniemy? smile.gif
Przydałby się pierwszy Interfejs, no tylko od czego zacząć.

Poznałem i przeanalizowałem ho, setki interfejsów, ale żebym który napisał. Ha ha mam okazje!
Jakie to proste, ale kiedy zacząć trzeba od podstaw?!

Jako, że aplikacja funkcjonuje już w pierwszej wymaganej opcji bez menedżera pakietów, można spróbować jak sobie radzi z Composerem.
Wgrałem PHPUnit, teoretycznie zainstalowane, ale gorzej z uruchomieniem testu, a przydałby się pierwszy test np. tego naszego połączenia z bazą danych.

Zaktualizowałem cały kod na Gicie z wersji 2.1 do 2.2 z nowym DI.
W logach miałem zero błędów. Przed wysłaniem sprawdziłem kod narzędziami od jakości kodu, wynik 0 błędów

Cytat(com @ 7.02.2024, 15:09:08 ) *
No i to jest pierwszy i podstawowy błąd...


Czyli kodowanie w czystym PHP uważasz za błąd? No, mając na względzie samą komercje, to masz racje, po prostu nie opłaca się pisać kodu od nowa kiedy są gotowce.
Czas jest cenny, ale pomijając komercyjne projekty może przyjmijmy, że mamy czas, ponieważ gotowce też nie wzięły się z powietrza.
Przyznam jednak, że jestem skłonny przyznać Ci racje bardziej niżeli słowom, które napisał Tomplus, ale generalnie macie podobnie racje smile.gif

To co może zakodujemy coś interesującego w PHP?!

Cytat
Chcę pomoc ale kompletnie nie zrozumiałeś przekazu, wysłałem Ci wartościowy materiał... Uparłeś się żeby stworzyć FW bez composera...


Dziękuje za chęci. Chyba nie oczekujesz, że ogarnę materiał, nad którym pracowałeś latami w tydzień (jednocześnie pisząc kod)... Uparłem się, no ok, ale nie prawdą jest że bez Composera itp. itd.
Obiecuję Ci smile.gif, że spróbuję ogarnąć przesłane materiały, ale kiedy przeczytam wszystkie treści, które chciałbym przeczytać to pójdę na psychologię, oczywiście programować sztuczną inteligencje No na psychologii to chyba wszystko można.

Cytat
Swoją droga nie można tego nawet uruchomić...

Sorki, nie ukończyłem prac nad routingiem, działa tylko na localhost w dowolnym katalogu, na domenie sypie się.

Cytat
Fajne logo...

Do logo wziąłem mapę Polski i jej obrys pokolorowałem na kolory, które pasowały do template.
Super, że fajne, a że wyszło podobne do JetBrains to nie grzech.

Cytat
To nie jest akurat SEO friendly...

Ok

Przesłałeś poprawki routingu, Wow. Spróbuję wdrożyć przesłany kod i zobaczę jak sobie poradzi w domenie.
Raczej przy weekendzie się tym zajmę, bo dzisiaj to już ogromny komercyjny zegar mnie goni.

Napisany przez: nospor 8.02.2024, 12:24:30

Cytat
Interfejsy. No to od czego zaczniemy? smile.gif
Przydałby się pierwszy Interfejs, no tylko od czego zacząć.

Nie rozumiem jak to od czego zaczac?

Tworzysz interfejs o nazwie np. DatabaseInterface
Twoja klasa Database implentuje ten interfejs
class Database implements DatabaseInterace


a wszedzie tam w konstruktorach czy gdzie tam miales (Database $database) to teraz masz miec (DatabaseInterface $database)

No to sa juz podstawy interfejsow.

Napisany przez: com 9.02.2024, 09:22:50

Cytat(Malinaa @ 8.02.2024, 11:44:53 ) *
Czyli kodowanie w czystym PHP uważasz za błąd? No, mając na względzie samą komercje, to masz racje, po prostu nie opłaca się pisać kodu od nowa kiedy są gotowce.
Czas jest cenny, ale pomijając komercyjne projekty może przyjmijmy, że mamy czas, ponieważ gotowce też nie wzięły się z powietrza.
Przyznam jednak, że jestem skłonny przyznać Ci racje bardziej niżeli słowom, które napisał Tomplus, ale generalnie macie podobnie racje smile.gif

To co może zakodujemy coś interesującego w PHP?!



Dziękuje za chęci. Chyba nie oczekujesz, że ogarnę materiał, nad którym pracowałeś latami w tydzień (jednocześnie pisząc kod)... Uparłem się, no ok, ale nie prawdą jest że bez Composera itp. itd.
Obiecuję Ci smile.gif, że spróbuję ogarnąć przesłane materiały, ale kiedy przeczytam wszystkie treści, które chciałbym przeczytać to pójdę na psychologię, oczywiście programować sztuczną inteligencje No na psychologii to chyba wszystko można.


Sorki, nie ukończyłem prac nad routingiem, działa tylko na localhost w dowolnym katalogu, na domenie sypie się.


Do logo wziąłem mapę Polski i jej obrys pokolorowałem na kolory, które pasowały do template.
Super, że fajne, a że wyszło podobne do JetBrains to nie grzech.


Ok

Przesłałeś poprawki routingu, Wow. Spróbuję wdrożyć przesłany kod i zobaczę jak sobie poradzi w domenie.
Raczej przy weekendzie się tym zajmę, bo dzisiaj to już ogromny komercyjny zegar mnie goni.


Nie uważam za kodowanie w czystym PHP za błąd, ale mówimy o zupełnie czymś innym, na tym polegać powinno kodowanie w czystym PHP https://tsh.io/blog/how-create-framework-agnostic-application-in-php/ tylko to nie ten poziom wiedzy, wspomniałem o tym wcześniej już smile.gif najpierw trzeba poznać jakiś FW by potem pisać kod niezależny od niego, pisanie w czystym PHP to nie tworzenie własnego FW, bo nawet jak dla jakiegoś projektu musisz stworzyć jakiś SharedKernel czy Core w czystym PHP to nadal nie jest to pełnoprawny FW jaki teraz próbujesz stworzyć. Po prostu powinieneś używać tego tam gdzie ma to sens smile.gif

Jasne że możemy zakodować, ale z użyciem np Symfony/Laravel, pisząc logikę jako framework-agnostic, nie DbM Framework wink.gif

Ten materiał nie jest stworzony przez mnie, polecam Ci tylko wartościowe źródła z internetu które warto przestudiować, tym bardziej biorąc się za tworzenie FW, bez tych podstaw to się nie uda dobrze smile.gif

Wystarczyło pozbyć się tej metody i działa smile.gif

Co do logo jasne ale jak coś jest zbyt podobne do siebie to jest plagiat, wiec to grzech smile.gif

Poprawiłem ale dla wersji poprzedniej, widziałem że zrobiłeś zmiany w wersji 2.2 wiec musisz sobie poprawić różnice smile.gif



Pytasz o interfejsy, bo znów nie znasz tego pojecie, przez co nie wiesz jak to przełożyć na kod, musisz najpierw poczytać potem próbować coś zastosować w własnym kodzie smile.gif Jak zrozumiesz pojecie interfejsu to łatwo przyjdzie Ci go stworzyć, np dla przypadku o którym wspomniał nospor

Napisany przez: Malinaa 9.02.2024, 11:38:31

Cytat(com @ 9.02.2024, 09:22:50 ) *
Nie uważam za kodowanie w czystym PHP za błąd... tylko to nie ten poziom wiedzy...


Pierwszy raz w tym poście słowo poziom jest jak najbardziej uzasadnione i tym razem bez dwóch zdań przyznam (mimo że nie powinienem) - masz racje.
Gdybym miał większą wiedzę, o której piszesz nie bawiłbym się frameworkami, a od razu zajął sztuczną inteligencją smile.gif
ale póki co pozwól, że stworzę kod, który jak rozumiem tylko dla mnie ma sens - jest mi potrzebny jak WordPress 60 procentom Internetu
(chociaż 10 lat temu ostatni raz użyłem tego kodu, bo kiedy go zobaczyłem i napisałem kilka wtyczek uznałem, że nie ma sensu iść tą "ślepą" drogą),
poza tym może zostańmy przy temacie postu frameworkach nie koniecznie autorskich wink.gif a Symfony i Laravel.
Powstaje odwieczne pytanie sensu... podążania tą drogą?

P.S. Dziękuje za przesłanie poprawek routingu, sprawdziłem śmiga w domenie,
jeszcze zajrzę jak chodzi na localhost, no i można poczytać o interfejsach itd... I nastała jasność.

Napisany przez: com 9.02.2024, 12:19:11

Cytat(Malinaa @ 9.02.2024, 11:38:31 ) *
Pierwszy raz w tym poście słowo poziom jest jak najbardziej uzasadnione i tym razem bez dwóch zdań przyznam (mimo że nie powinienem) - masz racje.
Gdybym miał większą wiedzę, o której piszesz nie bawiłbym się frameworkami, a od razu zajął sztuczną inteligencją smile.gif
ale póki co pozwól, że stworzę kod, który jak rozumiem tylko dla mnie ma sens - jest mi potrzebny jak WordPress 60 procentom Internetu
(chociaż 10 lat temu ostatni raz użyłem tego kodu, bo kiedy go zobaczyłem i napisałem kilka wtyczek uznałem, że nie ma sensu iść tą "ślepą" drogą),
poza tym może zostańmy przy temacie postu frameworkach nie koniecznie autorskich wink.gif a Symfony i Laravel.
Powstaje odwieczne pytanie sensu... podążania tą drogą?

P.S. Dziękuje za przesłanie poprawek routingu, sprawdziłem śmiga w domenie,
jeszcze zajrzę jak chodzi na localhost, no i można poczytać o interfejsach itd... I nastała jasność.


Żeby dziś znaleźć prace trzeba jeden z nich znać, a zakładam że kiedyś będziesz chciał pracować jako programista, skoro próbujesz się tego uczyć? FW to tylko narzędzie, jak młotek do wbijania gwoździ, jak nie będziesz potrafił jego obsługiwać to zrobisz sobie krzywdę, z FW jest tak samo. Jak już potrafisz go używać to wtedy czas na gwoździarkę i tu jest tak samo, poznajesz nowe rzeczy, zaczynasz pisać kod zgodny z ideą DDD, stajesz się framework-agnostic itp smile.gif

Super że działa, ciesze się i fajnie że zaczynasz słuchać o tym żeby poczytać smile.gif

Napisany przez: Malinaa 12.02.2024, 00:27:30

Cytat(nospor @ 8.02.2024, 12:24:30 ) *
Tworzysz interfejs o nazwie np. DatabaseInterface
Twoja klasa Database implentuje ten interfejs
class Database implements DatabaseInterace


Ogólnie jak działają interfejsy to wiem, w praktyce wyszło o czym nie wiedziałem, ale już też zrobione.
Interfejsy utworzone. Coś chyba jeszcze jest nie tak, ponieważ w np.

DatabaseInterface

public function queryExecute(string $query, ?array $params = [], bool $reference = false): bool;

kiedy zmienię na bool $reference bez wartości w Visualu pojawia się chmurka: Expected 3 arguments, Found 2.

Aplikacja działa ok, ale Visual coś pokazuje nie ok?


Cytat(com @ 9.02.2024, 12:19:11 ) *
Super że działa, ciesze się i fajnie że zaczynasz słuchać o tym żeby poczytać smile.gif

O interfejsach poczytane, chociaż powtarzają się w Necie te same podstawy i za wiele poza tym o czym napisał nospor się nie dowiedziałem, ale zawsze to coś nowego.

Routing jeszcze nie daje spokoju, ponieważ linki nie są SEO friendly, np. present-eusmod-grvd-lbero-luctus-ns-fermentum-nec,art,5.html
Jaka byłaby najlepsza opcja z końcówką .html, może /art/5/present-eusmod-grvd-lbero-luctus-ns-fermentum-nec.html
ale ze slashami w adresie i prawidłową ścieżką był zawsze kłopot w przeglądarce ?

Napisany przez: Salvation 12.02.2024, 09:24:26

Cytat(Malinaa @ 12.02.2024, 00:27:30 ) *
public function queryExecute(string $query, ?array $params = [], bool $reference = false): bool;

A po co Ci wartości domyślne w Interface? Rzadko się takie coś robi, bo powinno się świadomie wypełnić parametr.

Cytat(Malinaa @ 12.02.2024, 00:27:30 ) *
kiedy zmienię na bool $reference bez wartości w Visualu pojawia się chmurka: Expected 3 arguments, Found 2.

Aplikacja działa ok, ale Visual coś pokazuje nie ok?

Wartość domyśla pozwala pominąć argument przy wywołaniu metody. Dostajesz informację z IDE, że źle wywołujesz metodę.

Czyli w Twoim przypadku poprawne będzie skorzystanie z takich wywołań:
  1. queryExecute('query');
  2. queryExecute('query', null);
  3. queryExecute('query', null, true);
  4. queryExecute(query: 'query', reference: true); // dla PHP 8


A to czy "aplikacja działa ok", bez podania ostatniego parametru w metodzie, powinno wyjść przy testach, które pewnie napisałeś?

Napisany przez: Malinaa 12.02.2024, 20:27:50

Cytat(Salvation @ 12.02.2024, 09:24:26 ) *
1. Czyli w Twoim przypadku poprawne będzie skorzystanie z takich wywołań:
2. A to czy "aplikacja działa ok", bez podania ostatniego parametru w metodzie, powinno wyjść przy testach, które pewnie napisałeś?


1. Jasne, tylko po co w każdym wywołaniu metody pisać (dodawać false/true)
$this->database->queryExecute($query, [':limit' => $limit], false); ?
jeśli tak jest krócej i prościej $this->database->queryExecute($query, [':limit' => $limit]);

kiedy mimo, że rzadko się tak robi w interfejsie można dać: public function queryExecute(string $query, ?array $params = [], bool $reference = false): bool;
nie public function queryExecute(string $query, ?array $params = [], bool $reference): bool; i jest ok.

2. Dla testów dodałem ExampleTest.php wywołałem "composer test", aby sprawdzić czy działają i jest OK (1 test, 1 assertion), czyli testować też można.
Natomiast jeżeli chcesz mnie nauczyć pisania testów to możemy zrobić kolejny test np. DatabaseTest.php, ale to nie będzie takie proste.
No kto pisze testy wink.gif ale pewnie, że byłoby warto coś przetestować.

Może jakieś pomysły jak powinien wyglądać idealny seo friendly link dla przykładowego z końcówką .html
/art_5_present-eusmod-grvd-lbero-luctus-ns-fermentum-nec.html - z podkreślnikiem nie za bardzo wygląda, a ze slasem jest kłopotliwy ?
Wydawało się, że przecinek będzie zgodny z seo podobnie jak kropka, ale jak się okazuje, przecinek nie, więc jak by to zrobić..
Robiłem kiedyś split testy i wyszło, że największa klikalność była dla linków z końcówką typu domain.com/key-word-etc.html
a 10 % więcej potencjalnych klientów, może oznaczać że zamiast zarabiać 100 mamy 110, 1000 -> 1100, 10 000 -> 11 000 zł, więc spora różnica na plusie.
Przydałaby się konstrukcja linków, w które klikamy bez opamiętania i nie trzeba używać słów typu: best-sex-in-life.html smile.gif

Napisany przez: Salvation 13.02.2024, 11:34:58

Cytat(Malinaa @ 12.02.2024, 20:27:50 ) *
1. Jasne, tylko po co w każdym wywołaniu metody pisać (dodawać false/true)
$this->database->queryExecute($query, [':limit' => $limit], false); ?
jeśli tak jest krócej i prościej $this->database->queryExecute($query, [':limit' => $limit]);

kiedy mimo, że rzadko się tak robi w interfejsie można dać: public function queryExecute(string $query, ?array $params = [], bool $reference = false): bool;
nie public function queryExecute(string $query, ?array $params = [], bool $reference): bool; i jest ok.

Przyjęło się - i takie też są zalecenia - że ergumenty metody z domyślną wartością lądują na końcu. A nic nie staje na przeszkodzie, żeby Interface wyglądał po bożemu:
  1. public function queryExecute(string $query, ?http://www.php.net/array $params, bool $reference): bool;

A klasa, która go implementuje wyglądała tak:
  1. public function queryExecute(string $query, ?http://www.php.net/array $params = null, bool $reference = false): bool;

Demo: https://3v4l.org/00aAp

Cytat(Malinaa @ 12.02.2024, 20:27:50 ) *
2. Dla testów dodałem ExampleTest.php wywołałem "composer test", aby sprawdzić czy działają i jest OK (1 test, 1 assertion), czyli testować też można.
Natomiast jeżeli chcesz mnie nauczyć pisania testów to możemy zrobić kolejny test np. DatabaseTest.php, ale to nie będzie takie proste.
No kto pisze testy ;) ale pewnie, że byłoby warto coś przetestować.

Kto pisze testy? Przedewszystkim twórcy frameworków. Polecam podejście TDD. Znacznie mniej kodu się produkuje i - prawie - od razu wchodzisz w SOLIDy kod.

Napisany przez: Malinaa 13.02.2024, 20:46:10

Cytat(Salvation @ 13.02.2024, 11:34:58 ) *
A nic nie staje na przeszkodzie, żeby Interface wyglądał po bożemu:


No, o tym pisałem wcześniej, że staje na przeszkodzie, bo Visual wyświetla chmurkę, że brakuje argumentu,
dlatego dałem tak samo jak w klasie bool $reference = false.

Jeżeli w interfejsie dam bool $reference aplikacja działa, teoretycznie jest ok, ale Visual pokazuje nie ok.

Kiedy napiszę w klasie i interfejsie bool $reference = false nie ma problemu, wszystko na zielono Ok.
Chyba, że jest coś jeszcze, może jakoś powtarzają się metody, wczytywane są 2x te same?
Raczej nie chciałbym w każdej metodzie dodawać false: $this->database->queryExecute($query, [':limit' => $limit], false);
ale dlaczego kiedy zmienię interfejs na bez false: public function queryExecute(string $query, ?array $params = [], bool $reference): bool;
w Visualu świeci na czerwono?

Testy, to może przy weekendzie zobaczę, czy coś da się zrobić,
chyba, że byłbyś tak dobry i napisał przykład dla Database.php?
Byłoby prościej próbować kolejne.

Adres SEO Friendly - coś nie widać pomysłów?
Teoretycznie z tego co widzę można tworzyć linki z podkreśleniem, chociaż nie jest zalecane:
przykład: /art_5_present-eusmod-grvd-lbero-luctus-ns-fermentum-nec.html
trochę też tak sobie wygląda z tymi podkreśleniami, może jakiś inny znak (dozwolony w seo linku), alko ze slasem
/art/5/present-eusmod-grvd-lbero-luctus-ns-fermentum-nec.html fajnie wygląda, tylko tutaj mam kłopot taki, ze przeglądarka traktuje ścieżkę jakby strona (statycznie) była w katalogu /art/5/
i wówczas trzeba przerabiać path np. przy wyświetlaniu obrazków na ../../images/ aby wejść do katalogu, podobnie dla ../../assets/ oraz podstron, itd...
Czy jest jakieś sprytniejszy sposób? Aby mieś 100% SEO i nie kombinować z path'em!

Napisany przez: viking 14.02.2024, 09:24:11

Zaczynając od php 8.0 wszystkie parametry required muszą być zdefiniowane pierwsze.
A nawet jak zdefiniujesz public string $a, public ?string $b = null, public string $c to automatem php przerobi $b na wymagany.

Napisany przez: Salvation 14.02.2024, 15:21:12

Cytat(Malinaa @ 13.02.2024, 20:46:10 ) *
dlaczego kiedy zmienię interfejs na bez false: public function queryExecute(string $query, ?array $params = [], bool $reference): bool;
w Visualu świeci na czerwono?

Cytat(Salvation @ 13.02.2024, 11:34:58 ) *
Przyjęło się - i takie też są zalecenia - że argumenty metody z domyślną wartością lądują na końcu.


Jak zrobisz to w ten sposób co proponujesz, to `$params` będzie required (choć kod na to nie wskazuje), więc jak podasz tylko dwa argumenty w wywołaniu, to otrzymasz błąd w IDE. Ale widzę, że już Viking mnie uprzedził :D
Mam wrażenie też, że nie do końca ogarniasz OOP. Dlaczego uparłeś się mieć wartości domyślne w Interface?

Napisany przez: Malinaa 17.02.2024, 00:06:00

Cytat(viking @ 14.02.2024, 09:24:11 ) *
Zaczynając od php 8.0 wszystkie parametry required muszą być zdefiniowane pierwsze.


Nie inaczej required są pierwsze.

W przykładzie ostanie false mogło być mylące, dokładnie całość w interfejsie chciałem dać tak:

public function queryExecute(string $query, array $params, bool $reference): bool;

gdzie tylko $query jest required (jeden pierwszy argument)

ale wówczas np. w BlogModel.php Visual pokazuje chmurkę, że nie widzi wszystkich argumentów

dlaczego wyskakuje chmurka - ponieważ w BlogModel.php jest construct z DatabaseInterface i po zmianie na siłę trzeba wrzucać w metodę false
a chyba nie po to był pisany interfejs, aby w konstruktor wstrzykiwać klasę Database, albo w metodę nie wymagane false/true? Jak to napisał wcześniej Salvation.


Cytat
Jak zrobisz to w ten sposób co proponujesz...


Zmieniłem na sposób, który zaproponowałeś, bo "tak się przyjęło"... i elegancko. Aplikacja działa; kłopot, że w Visualu świeci na czerwono.

Kod jest dostępny. Jak nie ma kodu, to podaj kod, kiedy jest kod to nie można do niego zajrzeć?
Coś tu nie rozumiem.

Może lepiej podam kod krok po kroku, bo chyba można ten kod ze sobą "pogodzić".

Klasa Database.php

Kod
public function queryExecute(string $query, ?array $params = [], bool $reference = false): bool
{
...
}


Interfejs DatabaseInterface.php był tak samo jak w klasie, ale chcemy, aby był tak:

Kod
public function queryExecute(string $query, array $params, bool $reference): bool;
{
...
}


i ok, zmieniam na powyższy - aplikacja działa, teoretycznie wszystko ok, ale przechodzę do
modeli, np.: BlogModel.php -> Visual wyświetla chmurkę -> brakuje argumentów w metodzie queryExecute()

I tutaj jest problem, no ale w konstruktorze miałem: public function __construct(DatabaseInterface $database) -> brak zgodności więc Visual świeci na czerwono,
no dobrze, ale generalnie nie ma problemu zmieniam konstruktor na:

Kod
public function __construct(Database $database)
{
...
}


i wszystko działa, w Visualu też wszystko jest na zielono, ok.

Przy czym w kontrolerach np. BlogController.php w konstruktor wstrzykiwany jest interfejs

Kod
public function __construct(DatabaseInterface $database)
{
...
}



Pytanie, czy tak jest dobrze, że mając interfejs w konstruktor modeli wstrzykuję klasę Database nie interfejs DatabaseInterface,
ewentualnie jak prawidłowo pogodzić te klasy i metody z klasą Database i interfejsem DatabaseInterface?
Tutaj coś nie wiem, które rozwiązanie jest dobre.
Czy w modelach może tu być w konstruktorze Database zamiast DatabaseInterface, bo ta zamiana rozwiązuje problem wyświetlania przez Visual chmurki z info o brakujących argumentach metody.

Chciałbym ogarnąć jeszcze miejsca w kodzie, gdzie zostało dodane TODO!
I trafiam na taką funkcje, która to była jako tymczasowa dla szablonów i jest powiązana z Database, które jak widać z powyższego jeszcze szwankuje.

plik template.php

Kod
// TODO! Temporary function
use Dbm\Classes\Database;

function temp_htmlUser($sessionUserId, $module = null, $tempPath = null): string
{
    $database = new Database(); // TODO!
    ...
}


Jak to zrobić, aby było ok dla szablonów, w których chciałbym mieć tylko funkcje do obsługi szablonu, np. path() itp. gdzie temp_htmlUser() wymaga połączenia z bazą danych?

Napisany przez: nospor 17.02.2024, 11:21:45

Cytat
Jak to zrobić, aby było ok dla szablonów, w których chciałbym mieć tylko funkcje do obsługi szablonu, np. path() itp. gdzie temp_htmlUser() wymaga połączenia z bazą danych?

No toz dopiero co przerabialismy DI. Skoro jakas twoja tam funkcja potrzebuje obietku DB to masz go pokolei przekazywac az dojdzie do tej funkcji. Zasada sie nie zmienia.

To po pierwsze.
A po drugie funkcja template nie powinna nic wiedziec o bazie. Ona tylko powinna dostac dane niezbedne do wygnerowania szablonu

Napisany przez: Malinaa 17.02.2024, 20:09:45

Tak przerabialiśmy i wiem jak mógłbym to zrobić dla klasy, ale tu są tylko funkcje.

Jest plik index.php w folderze public, tu wczytuje:
require(BASE_DIRECTORY . 'application' . DS . 'template.php');
w pliku template.php dodaje funkcje dla szablonów
i idąc po kolei, pierwszy szablon base.phtml w folderze templates wstawiam jakie potrzebuje funkcje,
pierwsza z góry <?= trans('lang') ?> itd. i mogłoby tak być, aż dojdziemy do
templates/_include/navigation.phtml linia 52 funkcja echo temp_htmlUser() (gdzie temp oznaczyłem jako temporary -> TODO! nie template)
w tej funkcji docelowo htmlUser() potrzebne jest pobranie danych z bazy danych i tutaj mój system szablonów w wersji pierwszej już wymięka.

Czy można ten system zostawić na funkcjach i pobrać obiekt DB dla funkcji, czy tutaj już muszę przerobić cały system na klasę? Plik template.php z funkcjami na Class -> Template.php
Kłopot przy przerabianiu jest taki, że chyba będę musiał poprawić też wszystkie szablony, ponieważ wówczas w wymienionym
base.phtml już nie będę mógł zrobić tak <?= trans('lang') ?> jak bym chciał, a raczej <?= $object->trans('lang') ?>

I tak zaglądam jak to zrobić, aby było dobrze. Prawdopodobnie to co mam jeszcze w pliku public/index.php jako funkcje

### FUNCTIONS - application starting, template engine, etc.
require(BASE_DIRECTORY . 'application' . DS . 'start.php');
// Default template engine, you can change it to any other one
require(BASE_DIRECTORY . 'application' . DS . 'template.php');

i tak będzie trzeba zmienić na klasy, ponieważ z tymi funkcjami to za dobrze nigdy nie będzie?
Tylko to oznacza konieczność przerobienia też wszystkich szablonów .phtml, wywołania funkcji <?= trans('lang') ?> i każdej innej,
no chyba, że może zostać <?= trans('lang') ?> itd. oraz htmlUser() <- DB ?

Tak na to jeszcze raz zajrzałem i jak mam zaś po kolei przerabiać te szablony to mi ręce opadły, dobrze byłoby wiedzieć jak zrobić teki system szablonów, co by teraz zaś nie wałkować szablon po szablonie...

Napisany przez: nospor 18.02.2024, 09:13:36

Jak ty sobei lubisz komplikowac prace
I co z tego ze tu masz funkcje a tam miales klasy? Przeciez tam w klasach i tak przekazywales DB do funkcji, ktora sie nazywala __construct, ale to nadal byla funkcja.

Skoro teraz masz:
function temp_htmlUser($sessionUserId, $module = null, $tempPath = null): string

a potem wywolanie
echo temp_htmlUser($this->getSession('dbmUserId'));

To czemu nie zrobisz analogicznie z db?


function temp_htmlUser($sessionUserId, DatabaseInterfacce $db, $module = null, $tempPath = null): string

a potem wywolanie
echo temp_htmlUser($this->getSession('dbmUserId'), $this-getDatabase());

Skoro w kontrolerze masz metode getSession to przeciez mozesz tam dodac metode tez getDatabase(). Przeciez kontroler ma juz obiekt DB wiec metoda getDatabase musi go tylko zwrocic. I juz

Napisany przez: Malinaa 18.02.2024, 20:21:15

Tak jakoś mam, że i tu gdzie można by przyjąć, że nie ma już problemu, pojawiły się znaki zapytania komplikujące dokończenie dzieła wink.gif

Bo wiesz wcześniej to miałem te szablony z końcówką typu base.html.php i funkcje (trans(), path(), htmlUser(), dalej $this->getSession() itp.) w tych plikach wydawały mi się ok,
ale zmieniłem rozszerzenie szablonów na końcówkę base.phtml i tak się zawiesiłem na tym, czy te funkcje w tych plikach są w ogóle ok?!

Dzięki za info. Jeżeli funkcje są tu ok, to problem z głowy,
no może jeszcze tylko z takim drobiazgiem co zrobić
i "sprzątam" pozostałe znaki zapytania oznaczone w kodzie TODO!

Kod
use Dbm\Interfaces\DatabaseInterface;

function htmlUser(DatabaseInterface $database, int $sessionUserId, string $module = null): string
{
...
}


Jeśli w funkcji dam DatabaseInterface $database to muszę dodać use Dbm\Interfaces\DatabaseInterface co nie wygląda rewelacyjnie w tym pliku template.php.


Napisany przez: nospor 18.02.2024, 21:30:08

Cytat
Jeśli w funkcji dam DatabaseInterface $database to muszę dodać use Dbm\Interfaces\DatabaseInterface co nie wygląda rewelacyjnie w tym pliku template.php.

Skoro uzywasz DB w pliku template.php to nie masz wyjscia.

Napisany przez: Malinaa 19.02.2024, 20:34:23

Używam, choć przyszedł mi na myśl inny system szablonów. Jakiś gotowiec! Ciekawe, który by się tu nadawał najbardziej?

1.W tym pliku może pominę typowanie argumentów dla funkcji
function htmlUser($database, $sessionUserId, $module = null): string {}
wówczas nie trzeba dodawać 'use' co wygląda tu lepiej.

Kod prawie posprzątany, zostało zapytanie:

2. Czy można tu w zapytaniu użyć "SELECT roles FROM dbm_user WHERE id = ?"
czy bezpieczniej będzie "SELECT roles FROM dbm_user WHERE id = :id".
Jak to jest z :id lub znakiem zapytania w zapytaniu, czy tak samo bezpieczne?

Kod
public function userPermissions(int $id): ?string
    {
        $database = $this->database;
        $query = "SELECT roles FROM dbm_user WHERE id = ?"; // TODO! Jak to jest z :id lub znakiem zapytania, czy tak samo jest bezpieczne?
        ...
    }


3. Problem powstały przy napisaniu interfejsów. Jakby to zapytać możliwie najkrócej, które rozwiązanie wybrać.

Czy w
Kod
class BlogModel
{
    private $database;

    public function __construct(DatabaseInterface $database)
    {
        $this->database = $database;
    }

można wstrzyknąć: public function __construct(Database $database) klasę nie interfejs i będzie Ok?

Po tej zmianie można tez zmienić metody w interfejsie na pożądane, czyli bez wartości
public function queryExecute(string $query, ?array $param, bool $reference): bool;
zamiast
public function queryExecute(string $query, ?array $params = [], bool $reference = false): bool;
i jest czysto, bez błędów.
Natomiast jeżeli ma być DatabaseInterface $database w modelach to interfejsy zostają z wartościami,
bo nie wiem jak inaczej całość pogodzić.

Z tego co przejrzałem kod są to 3 ostatki do poprawienia oznaczone TODO i powinno być OK.
Bynajmniej na ten czas, no chyba, że przyjdzie pomysł na dalszą rozbudowę wink.gif

Napisany przez: nospor 20.02.2024, 09:57:23

ad2) bez znaczenia


Na pozostale dwa juz nie wiem czy mam sile odpowiadac.... Nie poto wprowadzilismy interfejsy zebys je teraz radosnie usuwal bo wizualnie ci sie nie podoba w templates masz use DB. No kurde.... juz ci dawno pisalem ze template nie powinien nic weidziec o DB, tylko dostac dane niezbedne do wygenerowania widoku i wtedy nie masz w ogole DB tam. No ale jak sie nie slucha to sie ma jak ma

Zas co do chmurek w twoim edytorze to tez nie ogarniam. Zmien edytor. Mi phpstorm nie pokazuje zadnuch chmurek czy bledow.

Nie ogarniam tez czemu tak na ciebie siedli za te wartosci defaultowe w interfejsie. A juz w ogole nie rozumiem czemu kaza ci default kasowac w interfejs a zostawic w klasie. No to juz w ooggole nieporozumienie, bo jak kiedys dojdzie inne klasa ktora implementuje interfejs ale tym razem bez defaultow tak mowi interfejs, to kod ci sie wywali bo jakies kawalki kodu beda jechac po starej klasie gdzie byly default. I zonk.

Napisany przez: Malinaa 20.02.2024, 22:34:50

2. Ok i super.

1. Ach, dasz radę. Ten template jest light weight smile.gif

Nie jest tak jak piszesz, albo coś nie kumam.
Zgodnie z tym co pisałem w template.php nie ma use DB nie ma też już use Interface,
usunąłem tylko typowanie z funkcji
function htmlUser($database, $sessionUserId, $module = null): string {}
nigdzie nie było powiedziane, że każda funkcja musi mieć typowanie, więc jest oki.

Wywołanie funkcji jest m.in. w navigation.phtml
echo htmlUser($this->getDatabase(), $this->getSession(''));
gdzie $this->getDatabase() jest w BaseController.php wstrzykiwane z interfejsu DatabaseInterface $database nie klasy DB (Database).

Chyba, że chcesz, aby zamiast $database, dać jakąś tablice z danymi usera: id, login, avatar itd...
No to akurat w przypadku tej funkcji "globalnej" (i używanej w różnych miejscach) jest mocno kłopotliwe, jeszcze nie wiem, ale raczej musiałby tu dodać jakiś nowy mechanizm,
coś jak w Symfony -> TwigExtension, albo coś za dużo sobie wyobrażam i można prościej.

Ale po co Extension itd. kiedy $this->getDatabase() idzie z interfejsu i załatwia sprawę?!

3. Czemu miałbym usuwać wartości defaultowe w interfejsie, które są w klasie - była informacja, że tak się przyjęło (no i mus, nie ma rady).
Może tak, program działa bez tych wartości, ale nawet Visual podpowiada, że po usunięciu domyślnych wartości coś nie do końca zagrało z interfejsem i klasą.
A piszesz, że lepiej jest dokładnie napisać interfejs zgodny z klasą też w defaultowych wartościach, co wbrew temu co się przyjęło będzie pomocne przed zonk'iem.
Pomijając Visual Twoja informacja utwierdza mnie w przerkonaniu, że te defaultowe wartości powinny tam być, wiec zostawiam interfejs tak jak utworzyłem.

Znalazłem jeszcze jedno TODO

Kod
private function paramType($value)
    {
        switch (true) {
            case is_null($value):
                return PDO::PARAM_NULL;
                break;
            case is_int($value):
                return PDO::PARAM_INT;
                break;
            case is_bool($value):
                return PDO::PARAM_BOOL;
                break;
            default:
                return PDO::PARAM_STR;
        }
    }

Jak zapisać typowanie dla tej metody, spróbowałem z mixed, ale wówczas się sypie
private function paramType(mixed $value): mixed ?

Napisany przez: nospor 21.02.2024, 09:18:19

Cytat
Nie jest tak jak piszesz, albo coś nie kumam.
Zgodnie z tym co pisałem w template.php nie ma use DB nie ma też już use Interface,

No nie ma bo usunales. O tym wlasnie mowie...nie po to wprowadzalismy interfejsy bys teraz radosnie je usuwal bo ci sie nie podoba use w twoim pliku.

Cytat
nigdzie nie było powiedziane, że każda funkcja musi mieć typowanie, więc jest oki.

X postow wczesniej plakales ze musi to byc zrobione perfekcyjnie,teraz cos usuwac bo cie sie wizualnie nie podoba use.

Ja mam dosc. Milego smile.gif

Napisany przez: Malinaa 24.02.2024, 23:15:43

Tak chcę, aby było bezbłędnie. Czy będzie perfekcyjnie jeśli dodam w pliku template.php use Dbm\Interfaces\DatabaseInterface? Myślę, że nie, jeżeli usunę też nie, więc po co się nad tym rozwodzić.
Pewnie, aby było dobrze i tak będę musiał dopisać klasę Template (zamiast tych funkcji), albo użyć gotowego silnika szablonów.
Wówczas rzeczywiście oddzielę ten widok tak jak wcześniej pisałeś i template nie będzie nic wiedzieć o DB.

Przy tym template trochę mnie odcięło (coś tam usunąłem, dodałem, już nie pamiętam), ale przypakowałem na siłce i mocy przybyło, jak nie siłka to Red Bull doda Ci skrzydeł.

P.S. Podaj adres podeślę Ci śniadanie. Jeżeli nie masz siły spróbuj coś innego, dobrego i poczujesz się o niebo lepiej smile.gif
Dzięki za pomoc. Pozdrawiam serdecznie

Dopisuje klasę TemplateEngine, wówczas można przyzwoicie dodać use Dbm\Interfaces\DatabaseInterface; kod umieściłem pod linkiem:
https://github.com/artimman/dbmframework/tree/main/_Documents/Template_v1
w pliku public/index.php odznaczam linie
//require(BASE_DIRECTORY . 'application' . DS . 'template.php');
przechodzę do BaseController w application/classes gdzie dodałem extends TemplateEngine

Sprawdzam jak działa templates/base.phtml i yield'y, include, extends jest ok, pozostało {{ $this->trans('lang') }}
Czy ktoś ma może pomysł jak sprawić, aby w szablonach działało {{ trans('lang') }} i/lub podobnie?
Zostało dopisanie mechanizmu, którym można wywoływać metody klasy jak funkcje w szablonach bez $this (->nazwaMetody).
Mam nadzieję, że wówczas i szablony będą zakodowane w porządku.

Napisany przez: nospor 25.02.2024, 20:33:19

Cytat
Sprawdzam jak działa templates/base.phtml i yield'y, include, extends jest ok, pozostało {{ $this->trans('lang') }}
Czy ktoś ma może pomysł jak sprawić, aby w szablonach działało {{ trans('lang') }} i/lub podobnie?


Jak zwykle nie kumam
Skoro ten kod

private function compileEchos(string $code): string
{
return preg_replace('~\{{\s*(.+?)\s*\}}~is', '<?php echo $1 ?>', $code);
}

ci obsluguje {{$this->metoda}}
to wywal $this i dodaj do echo

private function compileEchos(string $code): string
{
return preg_replace('~\{{\s*(.+?)\s*\}}~is', '<?php echo $this->$1 ?>', $code);
}


ps: dalej tworzysz potworki ala:

if (..) {
...
return ...
} else {
return ...
}

Nauki ciagle ida w las

Napisany przez: Malinaa 26.02.2024, 00:48:26

Gadanie, kumasz. Chociaż szczęście każdy rozumie na swój sposób smile.gif

return preg_replace('~\{{\s*(.+?)\s*\}}~is', '<?php echo $1 ?>', $code);

działa prawidłowo i jest potrzebne np. w pętli
{% foreach($colors as $color): %}
<li>{{ $color }}</li> <- jest elegancko
{% endforeach; %}
więc może nie ruszajmy tego co funkcjonuje dobrze.

na podanym kodzie
return preg_replace('~\{{\s*(.+?)\s*\}}~is', '<?php echo $this->$1 ?>', $code);
wyskakuje błąd

dopisałem taki kod

Kod
private function extensionPath(string $code): string
    {
        return preg_replace('~\{@\s*path(.+?)\s*\@}~is', '<?php echo $this->path($1) ?>', $code);
    }

    private function extensionTrans(string $code): string
    {
        return preg_replace('~\{@\s*trans/((.+?),(.+?),(.+?)/)\s*\@}~is', '<?php echo $this->trans($1,$2,$3) ?>', $code);
    }


w szablonie
{@ path('test/') @}
{@ trans('lang', [], []) @}
dla path() wygląda, że idzie, ale dalej przy funkcjach z wieloma argumentami trans() to nie wiem

poza tym trzeba te wszystkie metody wklepywać m.in. to compileCode(), co nie jest fantastycznym "mechanizmem",
ale nie widzę tu prostego rozwiązania i preg_replace() nie wystarczy, musiałby chyba tworzyć jakąś machine do obsługi takich custom funkcji?

No nie wygląda ten silnik szablonów rewelacyjnie z $this wszędzie, szablon jest zapchany $this'em,
ale jeżeli nie przeszkadza, nie obciąża systemu to może tak zostawić?


Powtórki jak się domyślam w class Database

Pytałem o function paramType($value), brak informacji na temat typowania dla takiej metody, gdzie return jest "mixed" - PHP 7, w 8 może wystarczyło by dać tylko mixed, chociaż z mixed to i tak nie wiadomo - skłoniło mnie do if and else, aby nie zostawiać metody bez typowania w klasie, gdzie każda metoda ma typowanie. Nauka nie idzie w las, takie pragmatyczne rozwiązanie. Poza tym to nie samolot, śmiga chociaż nie ma "skrzydeł".

Napisany przez: nospor 26.02.2024, 09:32:34

Cytat
wyskakuje błąd

Juz ci kiedys pisalem co masz zrobic gdy wyskakuje blad. I znowu, gadam w proznie

Cytat
Powtórki jak się domyślam w class Database

No wlasnie zle sie domyslasz. Nie mam czasu ogladac za kazdym razem calego twojego kodu. Skoro mowiles o szablonach to i ja spojrzalem tylko na szablony i tam wlasnie takie potworki byly

Napisany przez: Malinaa 28.02.2024, 18:51:51

Pamięć dobra, ale krótka o czym kiedyś pisałeś co zrobić gdy wyskakuje błąd. Takie podsumowanie, że już nie kumam.
Wolałbym się nad tym nie rozwodzić, but life is brutal and full of zasadzkas and sometimes kopas w dupas... tylko zgredzi nic nie kumają that there are beautiful moments in life, so let's enjoy life before old age hunt down you

Funkcje w szablonach mogą być jeszcze z wersji pierwszej, sprzed tego o czym pisałeś. Potem zobaczę do tych funkcji, przy optymalizacji i spróbuje według nowych wytycznych poprawić powtórki.
Na ten czas poprawiałem TODO w kodzie i wyszły te szablony do zrobienia.

Problem nie jest w samym błędzie, to można poprawić, ale poprawianie tego błędu nie wiele daje, gdyż funkcją preg_replace() której dotyczy błąd nie rozwiąże problemu rozszerzenia szablonów o dodatkowe funkcje, funkcjonalność.
Raczej przydałby się kolejny mechanizm dla takiego rozszerzenia. Chyba, że wystarczy poprawić preg_replace(),, dodać jakąś pętle itp. Wystarczy jedna sprytna metoda, nie potrzebnie tu kombinuje, bo nie wiem jak dopracować ten silnik szablonów?

Z tego co poczytałem $this w templates to nie kłopot, więc decyduje się na takie rozwiązanie, gdzie this/y będą tak <?= $this->trans('lang') ?>
https://github.com/artimman/dbmframework/blob/main/_Documents/Template_v1/templates/base.phtml
i powinno być Ok, lepsze niż kombinacje z preg_replace(), poza tym w znacznikach <?php ?> zawsze zadziała, natomiast kompilowane przez funkcje to już zależy na co trafi.

Dzięki dodaniu klasy TemplateEngine rozwiązany został problem w pliku template.php use Dbm\Interfaces\DatabaseInterface
oraz aplikacja rozbudowana została o takie mechanizmy jak kompilator szablonów i cache.

Jeżeli teraz szablony są Ok pozostały drobne pytania (oznaczone TODO)
1. private static $blocks = array(); Zastanawiałem się nad static array, ale powinno być Ok?
2. w Necie wyczytałem, że nie powinno się używać PHP_EOL należy użyć "/n"? Przy formatowaniu kodu HTML zakładam, że lepiej jest użyć PHP_EOL, wówczas powinno wykonać to samo w każdym systemie.

P.S. Usunąłem kilka powtórek else, które wyłapałem, że są do poprawienia.
Już sobie przypominam kiedy wyskakuje błąd, to wrzucić kod i komunikat błędu. Ok, ale tak tylko sobie "głośno" myślałem co z tym zrobić i problem rozwiązany.
Sorki, że dopytuje o takie szczegóły jak PHP_EOL, ale w Sieci jak taka ilość różnych informacji, że czasem trudno rozróżnić co jest dobre, a co nie.

Napisany przez: nospor 29.02.2024, 12:03:02

Cytat
Sorki, że dopytuje o takie szczegóły jak PHP_EOL, ale w Sieci jak taka ilość różnych informacji, że czasem trudno rozróżnić co jest dobre, a co nie.

A gdzie ty chcesz uzywac tego PHP_EOL? Bo ciezko mi sobie wyobrazic gdzie ci jest potrzebne

INna sprawa ze nie zastanawiasz sie nad kodem ktory piszesz. W wielu miejscach masz

<?= trans('charset') ?>
czyli wywolujesz funkcje trans() petryliard razy. A co robi funkcja trans?
za kazdym razem robi
require($pathTranslation);
no tak nie mozna.... Wczytaj plik raz, jego przerobine dane trzymaj w obiekcie i korzystaj z nich a nie za kazdym razem przerabiasz ten sam proces. A require to tylko jezna z nieicznych rzeczy ktore bez sensu powtarzac odpalajac trans()

Napisany przez: Malinaa 29.02.2024, 21:47:31

Użycie PHP_EOL w TemplateEngine

Kod
file_put_contents($cachedFile, '<?php class_exists(\'' . __CLASS__ . '\') or exit; ?>' . PHP_EOL . $code);


Kolejny kod, w którym chcę używać zamiast "\n", metody w TemplateFeature, np.

public function htmlUser(
...
$html .= '<img class="dbm-img-profile rounded-circle" src="' . $this->path() . 'images/avatar/' . $avatar . '">' . "\n"; // PHP_EOL powinno być tu lepsze, ponieważ wykona to samo w każdym systemie
...
)

i podobne metody z htmlem.

Faktycznie <?= trans('charset') ?> nie powinno tak być.

Najszybciej co przyszło mi na myśl:

Kod
class Translation implements TranslationInterface
{
    private $translation;

    public function __construct()
    {
        $this->translation = json_encode($this->translation());
    }

    /* Language translation */
    public function trans(string $key, array $data = null, array $sprint = null): string
    {
        $trans = json_decode($this->translation, true);
    }
}


Czy ten kod rozwiązuje problem, czy można lepiej?

Pewnie tak jest, że piszę trochę z "automatu", zwykle dopiero przy drugim i kolejnym podejściu do metody zastanawiam się nad nią pod względem optymalizacji.
Tak z miejsca optymalizować kod to chyba jeszcze nie na tym poziomie, ale fajnie byłoby od razu to widzieć.

Przy okazji będąc w class Translation poprawiłem else itp. o czym wcześniej pisałeś, chociaż czy klasa jest już optymalna?
Pewnie można lepiej, ale niestety może przy trzecim podejściu zobaczę więcej i będzie super optymalnie.

Napisany przez: nospor 1.03.2024, 09:40:08

Cytat
$this->translation = json_encode($this->translation());
$trans = json_decode($this->translation, true);

Czyli robisz json_encode raz, tylko po to, by potem za kazdym razem w metodzie trans petryliard razy robic json_decode. I teraz wytlumacz mi prosze, bo naprawde nie kumam, czemu nie mozesz tej tablicy przechowywac jako tablce, tylko musisz ja kodowac a potem dekodowac za kazdym razem. Jaka logika temu przyswiecala? Bo jakas na pewno musiala prawda?

Cytat
$html .= '<img class="dbm-img-profile rounded-circle" src="' . $this->path() . 'images/avatar/' . $avatar . '">' . "\n"; // PHP_EOL powinno być tu lepsze, ponieważ wykona to samo w każdym systemie

Przeciez tu generujesz kod html... tu w ogole nie trzeba nowej linii od tego zacznijmy. A nawet jesli by byla to co z tego ze serwer ci wygeneruje nowa linie wg systemu serwera, skoro klient ktory dostanie kod html moze byc na milionie innych systemow. wal \n i sobie glowy nie zawracaj

Cytat
file_put_contents($cachedFile, '<?php class_exists(\'' . __CLASS__ . '\') or exit; ?>' . PHP_EOL . $code);

To samo tutaj, to sa pliki cache, co ci zalezy czy bedzie tam \n czy PHP_EOL? TO nic nie zalatwia

Cytat
Przy okazji będąc w class Translation poprawiłem else itp. o czym wcześniej pisałeś, chociaż czy klasa jest już optymalna?

Nie wiem gdzie to poprawiles, na githubie zmian nie widze

Napisany przez: Malinaa 1.03.2024, 10:53:30

Chyba spanikowałem, na szybkości dodałem json_encode() kombinując z obiektem (kolejna kombinacja pod górkę, bo logiki w tym nie widzę).
Napisałeś "dane trzymaj w obiekcie". Z tą tablicą do obiektu, aby w funkcji używać tablicy - to json_encode() wink.gif
Chyba, że chcesz, aby przerobić całą metodę trans() na obiekcie, ale to nie wiem o co chodzi kiedy dane są w tablicy (w PHP 8 widzę coś chyba tematycznego enum{})

Czy nie może być po prostu:

Kod
public function __construct()
    {
        $this->translation = $this->translation();
    }

bo nie rozumiem o co chodzi z tym obiektem z reqiure array.

Przy PHP_EOL chcę się tu zdecydować na jedno PHP_EOL lub "\n", wcześniej dadawałem "\n" bo prościej niż pisać PHP_EOL, ale to daje w każdym systemie ten sam efekt, więc czy nie lepiej użyć PHP_EOL.
Załatwia to tyle, że przy wyświetleniu źródła strony jest czytelnie, a bez przejść do nowej linii mamy wszystko w jednej niekończącej się linii (za górami, za lasami... nie może tak być).

Nie było jeszcze kiedy poprawiać szablony pod nowy silnik, ale kilka poprawek przy silniku wczoraj przesłałem TemplateFeauture i Translation
https://github.com/artimman/dbmframework/tree/main/_Documents/Template_v1/application/classes

Napisany przez: nospor 1.03.2024, 10:57:07

Cytat
Napisałeś "dane trzymaj w obiekcie".

Chodzilo mi o obiekt translation, ze w nim masz trzymac dane, a nie ze dane maja byc obiektem

Cytat
Przy PHP_EOL chcę się tu zdecydować na jedno PHP_EOL lub "\n", wcześniej dadawałem "\n" bo prościej niż pisać PHP_EOL, ale to daje w każdym systemie ten sam efekt, więc czy nie lepiej użyć PHP_EOL.
Załatwia to tyle, że przy wyświetleniu źródła strony jest czytelnie, a bez przejść do nowej linii mamy wszystko w jednej niekończącej się linii (za górami, za lasami... nie może tak być).

Uzywaj \n i nie kombinuj, Juz ci zdaje sie to napisalem. to nie 20 lat temu ze trzeba bylo sie takimi duperami przejmowac bo kazdy system wszystko inaczej interpretowal. Teraz nowa linia to uniwersalne \n

Napisany przez: Malinaa 16.03.2024, 22:40:47

Przy sprzątaniu i optymalizacji natrafiłem na kod w public/index.php który był z wersji v1

isset($_GET['url']) ? $fileBasename = basename(str_replace('/', ',', $_GET['url']), '.html') : $fileBasename = 'index';
...
define('BASE_FILE', $fileBasename);

W obecnej wersji nie ma już $_GET['url'] a BASE_FILE jest używane tylko w jednym miejscu, więc nie ma potrzeby definiować.

W pliku start.php -> function reportingErrorHandler() dopisałem

Kod
$basename = 'index';
    $uri = $_SERVER["REQUEST_URI"];
    $dir = str_replace('public', '', dirname($_SERVER['PHP_SELF']));

    if ($uri !== $dir) {
        $basename = str_replace('.html', '', basename($_SERVER["REQUEST_URI"]));

        if (strpos($uri, '.') !== false) {
            preg_match('/\.(.*?)\./', $uri, $match);

            if (array_key_exists(1, $match)) {
                $basename = $match[1];
            }
        }
    }


i powyższe posprzątane, ale wyszło sporo zagnieżdżeń.

1. Szukam pomysłu jak uprościć kod?

System rozbudowany jest o rejestracje i logowanie, Admin po zalogowaniu może przejść do Panelu.
2. W panelu sporo jest w JS, ale pisałem w jQuery i AJAX, chyba należałoby zmienić na np. React?
A jeżeli React to pewnie przydałby się Webpack. No i pytanie jak to zrobić z tym kodem JS i AJAX, aby było super dobrze wink.gif

System szablonów zrobiony i szablony przerobione pod nowy system, zaktualizowane na Gicie:
https://github.com/artimman/dbmframework

Napisany przez: nospor 19.03.2024, 10:37:19

metoda trans nadal za kazdym razem gdy jest wywolywana odczytuje plik z tlumaczeniami.

Napisany przez: Malinaa 20.03.2024, 20:59:12

Metoda trans() w którym pliku/klasie?

W class Translation został dopisany konstruktor i wygląda Ok

public function __construct()
{
$this->translation = $this->translation();
}

Patrzę do class TemplateFeature - tutaj jest nie zmienione, ale sytuacja się komplikuje, ponieważ tu wszystko jest public dla szablonów
i nie wstrzyknę metod z class Translation -> private - będzie konflikt

Co zrobić, aby metod z class Translation -> translation() móc używać też w class TemplateFeature w obu klasach i nie powtarzać kodu.
Spróbowałem wstrzyknąć na różne sposoby, ale ciągle coś wyskakuje że musi być public?

Dla przykładu daję kod z metody do konstruktora

Kod
class TemplateFeature
{
    public $translation;

    public function __construct()
    {
        $cookieName = 'DbmLanguage';
        $languageDefault = 'pl';
        $arrayLanguages = explode('|', APP_LANGUAGES);

        !empty($arrayLanguages[0]) ? $language = $arrayLanguages[0] : $language = $languageDefault;

        if (!empty($_GET['lang'])) {
            $language = $_GET['lang'];

            if (strtolower($language) === 'off') {
                $language = $languageDefault;
            }
        } elseif (isset($_COOKIE[$cookieName])) {
            $language = $_COOKIE[$cookieName];
        }

        $pathTranslation = BASE_DIRECTORY . "translations/language." . strtolower($language) . ".php";

        if (file_exists($pathTranslation)) {
            $this->translation = include($pathTranslation);
        }
    }

}

...

public function trans(string $key, array $overwrite = [], array $sprint = null): string
    {
        if (!empty($this->translation)) {
            $translation = $this->translation;
            ....
        }

    }


otrzymuję komunikat:
Fatal error: Access level to App\Controller\IndexController::$translation must be public (as in class Dbm\Classes\BaseController) in IndexController.php on line 18

Nie chciałbym teraz dla tego $translation zmieniać wszystkie kontrolery private na public

Zmieniam w klasie public $translation; na public $publicTranslation; komunikat znika, oki, ale nie wczytuje tablicy if (!empty($this->publicTranslation)) jest empty, ścieżka ok itp. coś blokuje odczytanie tablicy?

- - -

Zmieniłem jednak kontrolery i parę rzeczy:
https://github.com/artimman/dbmframework/commit/3a283b7d110b672be31b44d4b8979ae418856695
Metoda poprawiona - wygląda Ok? I mniej kodu w kontrolerach, czyli też Ok.

Napisany przez: nospor 21.03.2024, 09:06:40

Tak, wlasnie o cos takiego mi chodzilo
public function __construct()
{
$this->arrayTranslation = $this->translation();
}




No ale czemu za kazdym razem tworzysz teraz znowu obiekt translation? Czemu nie stworzysz go raz na takiej samej zasadzie jak DataBase i nie przekazujesz gdzie trzeba?

Napisany przez: Malinaa 21.03.2024, 11:21:16

Konstruktor, o który chodziło to już był.

Obiekt translation tworzę raz w class BaseController extends TemplateEngine
Nie za bardzo chciałbym tworzyć $translation w index.php i przechodzić przez całość Router itd...
Po co jechać, aż od index.php, bo chyba o tym mowa!
Czy nie może zostać w BaseController?

Napisany przez: nospor 21.03.2024, 11:55:00

Cytat
Konstruktor, o który chodziło to już był.

Mi nie chodzilo o bycie konstruktora, tylko o to co w nim mialo byc...

Cytat
Obiekt translation tworzę raz w class BaseController extends TemplateEngine
Nie za bardzo chciałbym tworzyć $translation w index.php i przechodzić przez całość Router itd...
Po co jechać, aż od index.php, bo chyba o tym mowa!
Czy nie może zostać w BaseController

Obiekt tranlation tworzysz w wielu miejscach, nie tylko w BaseController. Wiec nadal wielokrotnie powielasz te same akcje, ala czytanie tego samego pliku,
I wracamy znowu dotego samego, niby chcesz porzadnie a potem i tak po lebkach robisz. Sie zdecyduj

Napisany przez: Malinaa 21.03.2024, 13:39:53

Ok, rozumiem o czym mówisz, kilka tygodni temu zostało już zrobione tak jak miało być...
teraz tylko zmieniłem nazwę $this->translation = $this->translation(); na $this->arrayTranslation - to już było zrobione na to co w nim miało być.

Cytat
Obiekt tranlation tworzysz w wielu miejscach...


Faktycznie był tworzony jeszcze w modelach LoginModel i RegisterModel, poprawiłem jest już tylko w BaseController.
Czy teraz jest Ok, czy trzeba jechać od index.php? Jeśli tak to nie rozumiem, dlaczego aż od index.php .
Co w przypadku np. new PHPMailer() obiekt jest tworzony wiele razy jeśli podstrona wymaga obsługi email,
ale jak rozumiem optymalnie będzie $translation obiekt utworzyć jeden raz.
Zmiana $transloation (obiekt utworzony tylko jeden raz w BaseController):
https://github.com/artimman/dbmframework/commit/2dd8a892f09de0099ff36039eb186fb927320b16

Zastanawia mnie jeszcze:
Kod
class TemplateFeature
{
    public $translation;
    ...
}
to "public", z którym był kłopot musiałem tutaj dodać

Napisany przez: nospor 21.03.2024, 17:03:33

Cytat
Jeśli tak to nie rozumiem, dlaczego aż od index.php .

Ja nie sledze dokladnie twojego kodu. DataBase musialo byc od index, potem widziale mze translation tez tworzysz w kilku miejscach wiec tez wydalo sie ze ma byc od index. Generalnei chodzi o to by nie tworzyc tego samego obiektu milion razy co ci sie zdarzalo.


Cytat
Zastanawia mnie jeszcze:
Kod
class TemplateFeature
{
public $translation;
...
}
to "public", z którym był kłopot musiałem tutaj dodać

czemu nie ustawisz tego translation w construktorze popprostu?

Napisany przez: Malinaa 27.03.2024, 15:09:11

Cytat
Generalnei chodzi o to by nie tworzyc tego samego obiektu milion razy co ci sie zdarzalo.

Jasne. Dzięki, że zwróciłeś na to uwagę, bo gdzieś mi się "zapodziało". 1 Milion smile.gif

class TemplateFeature jest taką trochę inną klasą "zewnętrzną" (wszystko musi być public), wszystko stąd idzie tylko w szablonach
i o dziwo ? nie ma potrzeby tworzenia konstruktora, ustawiam tylko public $translation;
i w metodzie trans() o której pisałeś że ładuje tablice za każdym razem daje
!empty($this->translation->arrayTranslation) ? $translation = $this->translation->arrayTranslation : $translation = null;
i całe to wielokrotne ładowanie tej samej tablicy mogłem usunąć (trochę dziwne, no ale działa bez błędu) i jest zoptymalizowane.

Była informacja, aby wykonać jeszcze testy w PHPUnit najlepiej TDD.

Tworzę class DatabaseTest {} i chciałbym przetestować połączenie i metody klasy Database w application/classes/.
szukam informacji w Necie, próbuję napisać test, ale nic sensownego nie wychodzi.
Jak napisać testy jednostkowe TDD z PHPUnit?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)