Agape
3.03.2010, 09:32:21
Rzecz banalna i potrafię ją zrobić tylko jak? Chodzi mi o komunikaty zwrotne typu:
nie prawidłowe hasło
pomyślne zalogowanie
haslo zostało wysłane na e-mail
produkt został dodany do koszyka / usuniety z koszyka
Można to zrobić prosto (i zarazem pewnie źle) przesyłając komunikat przez post/get na strone główną, ale dzięki temu ktoś może spreparować link wstrzykując swoją treść na strone główną.
Można też zrobić listę komunikatów, ale skoro jest z parenaście komunikatów czy takie rozwiązanie jest optymalne ? i jak przesyłać komunikaty ? np adres.pl?komunikat=45 (czyli np "pomyslnie zalogowano"), wtedy tez teoretycznie ktoś może spreparować link, tylko ze bardzo nie wiele mu to da, a gdy jeszcze komunikaty są przez post przesylane...
Nie mam obeznania w tym jakie techniki się stosuje dlatego to pytanie jak najlepiej to zrobić. Druga opcja brzmi rozsądnie ale chce się upewnić zamiast stosować "złe" praktyki
blooregard
3.03.2010, 09:38:20
A po co przesyłać komunikat?
Przykład dla logowania:
- jeśli f-cja dokonująca logowania pomyślnie zaloguje usera, zwraca np. message_id = 1;
- w pliku wyświetlającym stronę masz tablicę asocjacujną, gdzie kluczem jest id komunikatu, a wartością jego treść, sprawdzasz, co zwróciłą ta f-cja i w zależności od tego wyświetlasz w określonym miejscu odpowiedni komunikat:
$messages = array(1 =>'Zalogowany' , 2=>'Błąd, nie zalogowany' , .... ( inne komunikaty
)...
... tu obsługa logowania ...
$result = login();
...
reszta treści
...
Agape
3.03.2010, 10:11:33
Znaczy się wszystkie funkcje mam w osobnych plikach, pogrupowane tematycznie np w pliku user mam logowanie, wylogowanie, rejestrowanie, zmiana danych, wyslanie przypomnienia hasla więc przy takiej strukturze muszę coś wysłać. Chyba, że zmienić strukturę, nie wiem może źle to robię ale wszystkiego wrzucić do index.php nie da rady, nikt nie chciał by w takim pracować.
Tak więc mam np logowanie -> form post -> user.php?akcja=logowanie + $_POST gdzie jest user i haslo -> Header('Location: index.php') i tu chyba muszę dodać np ?message_id=1 o to chodziło prawda?
teoretycznie ktoś będzie mógł sobie wpisać na tej stronie głównej "logowanie udane" z byle jakiego adresu chociaż mało to mu da
blooregard
3.03.2010, 10:18:02
Nie, nie o to.
Logowanie realizujesz poprzez jakąś funkcję, tak? (albo metodę w klasie). Ona coś zwraca. Wystarczy, że w przypadku powodzenia operacji zwróci ID komunikatu 'Zalogowany', a w przypadku falsa ID komunikatu 'Błąd logowania'.
Tablicę z komunikatami błędów możesz sobie zdefiniować w osobnym pliku i inkludować w index.php.
Nic nie musisz dodawać do GET lub POST. Funkcja się wykonuje, zwraca rezultat wykonania do index.php, przez któego jest wywoływana, a tam, czy w pliku wyświetlającym stronę, dajesz echo $tablica_z_komunikatami[$id_Zwroconego_komunikatu] i po zawodach.
Agape
3.03.2010, 10:53:41
Dobrze że zadałem pytanie by zrobił bym to strasznie po lamersku. Dzięki bardzo za pomoc a skoro już spytałem się o to to może pomożesz mi jeszcze w 2 sprawach w stylu jak to dobrze zrobić, ponieważ zawsze przesyłałem między stronami zmienne ale z Twoich sugestii widzę, że robiłem chyba źle. Ogólnie cały plik user.php i resztę mam zrobioną tak:
if (isset($_POST['mail'], $_POST['haslo'])){ logowanie...
}elseif(isset($_GET['akcja']) && $_GET['akcja']=='zapisz'){ ...
}elseif(isset($_GET['akcja']) && $_GET['akcja']=='zmien_dane'){ ...
}elseif(isset($_GET['pokaz']) && $_GET['pokaz']=='rejestracja'){ ...
}elseif(isset($_GET['pokaz']) && $_GET['pokaz']=='edytuj'){ ...
}elseif(isset($_GET['pokaz']) && $_GET['pokaz']=='przypomnij-haslo'){ ...
}else echo 'nie przeslano odpowiednich danych';
dodatkowo w index php includuje wszystko przez switch ale to chyba nie jest już tak źle:
if(isset($_GET['pokaz'])){ switch($_GET['pokaz']){
case 'cennik':
include 'include/wyswietl_text.php'; break;
case 'produkty':
include 'include/produkty.php'; break;
case 'gorne':
include 'include/wyswietl_text.php'; break;
case 'rejestracja':
include 'include/user.php'; break;
case 'logowanie':
include 'include/user.php'; break;
case 'edytuj':
include 'include/user.php'; break;
case 'przypomnij-haslo':
include 'include/user.php'; break;
default:
include 'include/pasek_nawigacyjny.php'; pasek_nawigacyjny();
$strona_glowna = mysql_query('SELECT `zawartosc` FROM `menu` WHERE `typ`="glowna" AND `nazwa`="glowna"'); echo $zawartosc_glowna['zawartosc']; break;
}
}else{
include 'include/pasek_nawigacyjny.php'; pasek_nawigacyjny();
$strona_glowna = mysql_query('SELECT `zawartosc` FROM `menu` WHERE `typ`="glowna" AND `nazwa`="glowna"'); echo $zawartosc_glowna['zawartosc']; }
$_GET['pokaz'] jest jak coś includuje bezpośrednio na index.php a $_GET['akcja'] jak otwieram plik bezpośrednio i przekierowywuje później co już wiem, że jest źle ale chodzi o ogólną konstrukcje, bo rozumiem, że zamiast
if ... elseif ....elseif .... else można to zrobić "po ludzku" z funkcjami, ale jak ? Mam 2 pliki ze skryptami, user.php i koszyk.php gdzie jest po 250 i 500 linijek reszta plików jest po jednej funkcji, tak więc myśle tak:
include user.php
switch($_GET['user']){
case 'rejestracja':
$result = rejestracja(); break
case 'logowanie':
$result = logowanie; break;
case 'edytuj':
$result = edytuj(); break;
case 'przypomnij-haslo':
$result = Przypomnij_haslo(); break;
}
}
Wiem, że dla Ciebie to pewnie "rzeźnia" mój pierwotny kod, ale jak się uczyłem php nigdzie nie czytałem jak "powinno się pisać", po prostu uczyłem się kodu i sam kombinowałem. Jestem w trakcie pisania pierwszej dużej strony i pytam ponieważ jak się nabierze złych nawyków, te nawyki często zostają.
blooregard
3.03.2010, 11:30:33
To może inaczej, ja Ci napiszę, jak ja to robię.
Przede wszystkim jedynym wywoływanym plikiem jest index.php. W url-u przekazuję jedynie nazwę podejmowanej akcji, subakcji i, ewentualnie, id czegoś tam, co jest parametrem dla danej akcji (przykładowo, id produktu w sklepie).
Dla każdej akcji mam klasę, która ją obsługuje. Przykładowo, mamy akcję 'news', obsługującą newsy.
url w postaci 'index.php?action=news&subaction=showall' powoduje, że w pliku index.php inkludowana jest klasa NewsController.Class.php. W tej klasie w konstruktorze, zdefiniowana jest tablica dostępnych subakcji (np. 'showall', 'shownews' itp.). W zależności od parametru 'subaction' wywoływana jest odpowiednia metoda klasy (np. dla 'showall' jest to showAllNews() - pobiera i wyświetla wszystkie newsy.
Samo pobieranie, wyświetlanie i dodatkowe rzeczy obsługiwane są przez dwie dodatkowe klasy: NewsModel.Class.php (ona odpowiada za pobieranie danych z bazy) i NewsView.Class.php, która z kolei formatuje wyniki i wyświetla je. Klasy te przekazują sobie wzajemnie wyniki wywołań swoich metod, w rezultacie wszystko w postaci kodu HTML/Smarty trafia do szablonu index.tpl, który to wyświetla całość na stronie.
Powyższy opis jest uproszczony, bo do tego dochodzą klasy odpowiedzialne za obsługę tablic GET,POST, sesji, zapytań do bazy, tzw. helpery realizujące dodatkowe funkcjonalności (przykładowo paginacja wyników), całość działa w oparciu o jedną, główną klasę ApplicationClass odpowiedzialną za "kontrolę" nad całością.
Jest też klasa Message.Class.php, która m.in. realizuje obsługę komunikatów w opisany przeze mnie wcześniej sposób (zwraca treśc komunikatu dla otrzymanego z innych metod ID).
Powyższy sposób pozwala na:
- zdefiniowanie jednego, globalnego "wejścia" do aplikacji, co sprawia, że nie masz tysiąca osobnych plików odpowiedzialnych za wyświetlanie poszczególnych stron
- dodanie obsługi nowej akcji/subakcji sprowadza się do dodania odpowiednich metod w 3 klasach
- wszystkie parametry z GET/POST/REQUEST obsługujesz w jednym miejscu (walidacja, sprawdzanie dopuszczalnych zakresów wartości itp.), co chroni Cię przed pominięciem jakiegoś parametru z url-a, któremu zapomnisz dodać np. rzutowania i masz gotową dziurę do SQL Injection
- klasa odpowiedzialna za generowanie linków (też taka jest) pozwala na globalne ustawienie wyglądu url-i, co ułatwia utworzenie przyjaznych linków obsługiwanych przez mod_rewrite
- w jednym miejscu możesz skupić metody sprawdzające na wejściu, czy np. jest zalogowany jakiś user, adres referrera jest poprawny itd. itp. co eliminuje taką konieczność w przypadku budowania aplikacji opartej na jedna strona = osobny plik
Schemat działania czegoś takiego jest prosty:
index.php?action=news&subaction=showall (lub, przykładowo, index.php/news/showall z wykorzystaniem mod_rewrite)
|
V
ApplicationClass <- ładuje klasy bazowe (DbClass, SessionClass, RequestClass) i tworzy ich instancje; na podstawie 'action' ładuje
i tworzy instancję kontrolera dla akcji News (NewsController.Class.php)
|
V
NewsController.Class.php <- odczytuje parametr 'subaction', w zależności od niego wywołuje soją metodę (np. showAllNews() )
|
L----> showAllNews() -> wywołuje metodę getAllNews() z klasy NewsModel.Class.php, ta pobiera newsy używając
prz okazji helpera Pager.Class.php (np. pierwsze 20 newsów) i zwraca
do showAllNews()
|
L----->ta metoda przekazuje do klasy NewsView.Class.php listę 20 pierwszych newsów
|
V
formatuje wynik, przypisuje do zmiennych Smarty
|
V
metoda showAllNews() w klasie NewsController.Class.php wywołuje metodę
displayPage(), ta generuje index.tpl z uwzgędnieniem tego, co wcześniej
NewsView.Class.php "stworzył"
Schemat jest, jak już wcześniej napisałem, nieco uproszczony, ale wyjaśnia ideę rozwiązania.
Agape
3.03.2010, 13:23:31
Dzięki bardzo za obszerne wyjaśnienie, właśnie dokładnie czegoś takiego potrzebowałem. Szkoda, że w żadnych pseudo kursach czy nawet w helionie nie znalazłem tego ale to pewnie dla tego, że na razie pisze strukturalnie chociaż jak to napiszę przejdę na OOP albo odrazu jakiś sprytny prosty framework z MVC (kohanaphp np. na początek nie jest zły chyba) w których orientuje się o co biega ale to jak skończę to się zajmę tym. Bardzo mi pomogłeś więc dzięki bardzo, poprzednie moje rozwiązania były trochę śmieszne

nieefektywne i "niewygodne" teraz będzie DUŻO lepiej. Jeszcze raz dzięki

.
To może ja powiem jak z kolei ja implementuję swoje rozwiązanie w oparciu o tablicę. Najczęściej to wykorzystuje przy formularzach, ale nie musi to być tylko tak stosowane. Mianowicie mam sobie stronę na której wykonuję różne akcje. Na początku kodu Tworzę sobie tablicę $messages jako pustą. w razie zrobienia czegoś co ma być sygnalizowane dodaję do niej element o 2 polach: typie oraz komunikacie. Typ definiuje mi stopień "błędu" może to być tylko informacja, może być komunikat błędu, albo wiadomość o powodzeniu czegoś. W efekcie moja struktura wyglądać może mniej więcej tak:
[0
] => array( 'ok', 'Coś się powiodło' ), [1
] => array( 'err', 'Coś spaprałeś' ), [2
] => array( 'inf', 'Tutaj możesz zrobić to, albo to') );
I na sam koniec oczywiście podczas wyświetlania te dane ujawniam. A w sytuacji gdy jest to część walidacji, zliczam wszystkie tablice z polem 'err' i jeśli jest choć jedno - zgłaszam błąd formularza oraz oddaje go do poprawki

Problem niby z przesłaniem tego poza obręb jednej, konkretnej strony? Ależ skąd! Od czego mamy sesje? Puszczamy tę tablicę w sesję i na innej stronie tylko wyświetlamy komunikaty, jednocześnie ową tablicę czyszcząc by komunikaty nie były pamiętane (no chyba, że chcesz je pokazywać cały czas). Przydatne w sesji gdyż uniezależniasz się od tablicy w obrębie jednego pliku i na dodatek możesz przesłać więcej niż 1 komunikat jak ma to miejsce w przypadku komunikatu poprzez id

Wystarczy, że każda strona serwisu implementuje wyświetlanie komunikatów z sesji i masz po prostu bajkę. Rzucasz do tej tablicy jakie chcesz komunikaty userowi, a nie tylko z góry zdefiniowane. Jeśli wiesz, że taki moduł w serwisie już masz, to piszesz tylko używając jego metod na zasadzie addError( $string), addInfo( $string ) czy addOk( $string). W ten sposób możesz przepychać przykładowo informacje o wylogowywaniu, zalogowaniu, błędach walidacji, polach informacyjnych czy co tylko Ci się zamarzy. A zrobienie z tego fajnej klasy do obsługi komunikatów w całym serwisie jest całkiem wygodne pod katem rozwojowym. Z czasem możesz to przecież rozszerzać do własnych potrzeb Kto powiedział, że masz mieć tylko 3 rodzaje komunikatów (info, error, ok) ?
blooregard
3.03.2010, 15:00:09
@thek, ja robię coś takiego:
Mam klasę odpowiedzialną za tworzenie formularzy (helper inkludowany przez AkcjaController)
W niej jest metoda createForm(), któa jako jeden z argumentów (poza action, metod, enctype itp.) przyjmuje tablicę z komunikatami błędu (jedna ze składowych klasy AkcjaController).
W klasie AkcjaModel, w któej znajduje się metoda walidacji formularza, mam coś takiego:
public function validateForm($formdata) {
if (!$formdata['imie']) {
$errors['imie'] = "Proszę podać imię";
}//if
...i tak dla wszystkich pol...
...a na końcu:...
if (!empty($errors)) return array(false, $errors); return array(true, $formdata); }
Metoda z klasy AkcjaController, która "zgłosiła" formularz do walidacji odbiera powyższy rezultat:
...
$is_valid = $this->model->validateForm($this->formdata);
if ($is_valid[0]) {
//wykonaj akcje, korzystajac ze zwalidowanych danych; czasem podczas walidacji zachodza dodatkowe dzialania
//na danych z formularza, dlaetgo uzywam tu danych zwroconych z validateForm()
$this->model->updateData($is_valid[1]);
} else {
//w przypadku niepowodzenia, do tablicy $errors klasy AkcjaController przypisuje tablice z bledami, ktora zwrocila
//validateForm() z AkcjaModel
$this->errors = $is_valid[1];
//wyswietlam formularz, w odpowiednich miejcach pojawiaja sie komunikaty dla odpowiednich pol
//w tym przykladzie, obok pola 'Imię' pojawi się 'Proszę podać imię'
$this->form->displayForm();
}
Metoda wyswietlająca formularz "wie", któe pole dostało komunikat błędu, bo klucz w tablicy $this->errors (tu:['imie']) jest taki sam, jak nazwa pola i wstawiany jest tam "z automatu".