Pierwsza odpowiedź: Kluczową rolą modelu jest utrzymanie integralności. Jednak przetwarzanie danych wejściowych użytkownika jest obowiązkiem administratora.
Oznacza to, że administrator musi przetłumaczyć dane użytkownika (które przez większość czasu są tylko ciągami znaków) na coś znaczącego. Wymaga to parsowania (i może zależeć od takich rzeczy jak lokalizacja, biorąc pod uwagę, że na przykład istnieją różne operatory dziesiętne itp.).
Tak więc faktyczna walidacja, jak w „czy dane są dobrze uformowane?”, Powinna być wykonana przez kontrolera. Jednak weryfikacja, jak w „czy dane mają sens?” należy wykonać w ramach modelu.
Aby wyjaśnić to na przykładzie:
Załóżmy, że Twoja aplikacja pozwala na dodanie niektórych encji z datą (na przykład problem z terminem). Możesz mieć interfejs API, w którym daty mogą być reprezentowane jako zwykłe uniksowe znaczniki czasu, a gdy pochodzą ze strony HTML, będzie to zestaw różnych wartości lub ciąg znaków w formacie MM / DD / RRRR. Nie chcesz tych informacji w modelu. Chcesz, aby każdy kontroler indywidualnie próbował ustalić datę. Jednak gdy data jest następnie przekazywana do modelu, model musi zachować integralność. Na przykład rozsądne może być niedopuszczanie dat z przeszłości lub dat przypadających w święta / niedziele itp.
Twój kontroler zawiera reguły wprowadzania (przetwarzania). Twój model zawiera reguły biznesowe. Chcesz, aby reguły biznesowe były zawsze egzekwowane, bez względu na to, co się stanie. Zakładając, że masz reguły biznesowe w kontrolerze, musisz je zduplikować, jeśli kiedykolwiek stworzysz inny kontroler.
Druga odpowiedź: Podejście to ma sens, jednak metoda może zostać wzmocniona. Zamiast ostatniego parametru będącego tablicą, powinna to być instancja IContstraint
zdefiniowana jako:
interface IConstraint {
function test($value);//returns bool
}
A dla liczb możesz mieć coś takiego
class NumConstraint {
var $grain;
var $min;
var $max;
function __construct($grain = 1, $min = NULL, $max = NULL) {
if ($min === NULL) $min = INT_MIN;
if ($max === NULL) $max = INT_MAX;
$this->min = $min;
$this->max = $max;
$this->grain = $grain;
}
function test($value) {
return ($value % $this->grain == 0 && $value >= $min && $value <= $max);
}
}
Szczerze mówiąc, nie rozumiem, co 'Age'
ma reprezentować. Czy to rzeczywista nazwa nieruchomości? Zakładając, że istnieje domyślna konwencja, parametr może po prostu przejść do końca funkcji i być opcjonalny. Jeśli nie zostanie ustawiony, domyślnie będzie to_nazwa_kameli w nazwie kolumny DB.
Zatem przykładowe wywołanie wyglądałoby następująco:
register_property('age', new NumConstraint(1, 10, 30));
Korzystanie z interfejsów polega na tym, że możesz dodawać coraz więcej ograniczeń w miarę upływu czasu i mogą one być tak skomplikowane, jak chcesz. Dla ciągu pasującego do wyrażenia regularnego. Aby data była co najmniej 7 dni wcześniej. I tak dalej.
Trzecia odpowiedź: każda jednostka modelu powinna mieć taką metodę Result checkValue(string property, mixed value)
. Administrator powinien zadzwonić przed ustawieniem danych. Result
Powinny mieć wszystkie informacje na temat tego, czy kontrola zawiodła, aw przypadku tak było, uzasadnienia, więc sterownik może propagować tych do widoku odpowiednio.
Jeśli do modelu zostanie przekazana niewłaściwa wartość, model powinien po prostu zareagować, zgłaszając wyjątek.
Nie zgadzam się całkowicie z „back2dos”: zalecam, aby zawsze używać osobnej warstwy formularza / sprawdzania poprawności, której kontroler może użyć do sprawdzania poprawności danych wejściowych przed przesłaniem ich do modelu.
Z teoretycznego punktu widzenia sprawdzanie poprawności modelu działa na zaufanych danych (wewnętrzny stan systemu) i idealnie powinno być powtarzalne w dowolnym momencie, podczas gdy sprawdzanie poprawności danych jednoznacznie działa raz na danych pochodzących z niezaufanych źródeł (w zależności od przypadku użycia i uprawnień użytkownika).
Ta separacja umożliwia budowanie modeli, kontrolerów i formularzy wielokrotnego użytku, które można luźno połączyć poprzez wstrzykiwanie zależności. Potraktuj walidację danych wejściowych jako walidację białej listy („zaakceptuj znane dobro”), a walidację modelu jako walidację czarnej listy („odrzuć znane złe”). Sprawdzanie poprawności na białej liście jest bezpieczniejsze, podczas gdy sprawdzanie poprawności na czarnej liście zapobiega nadmiernemu ograniczeniu warstwy modelu do bardzo szczególnych przypadków użycia.
Niepoprawne dane modelu powinny zawsze powodować zgłoszenie wyjątku (w przeciwnym razie aplikacja może kontynuować działanie bez zauważenia błędu), podczas gdy nieprawidłowe wartości wejściowe pochodzące ze źródeł zewnętrznych nie są nieoczekiwane, ale raczej powszechne (chyba że masz użytkowników, którzy nigdy nie popełniają błędów).
Zobacz także: https://lastzero.net/2015/11/why-im-using-a-separate-layer-for-input-data-validation/
źródło
Tak, model powinien przeprowadzić walidację. Interfejs użytkownika powinien również zweryfikować dane wejściowe.
Wyraźnie obowiązkiem modelu jest określenie prawidłowych wartości i stanów. Czasami takie zasady często się zmieniają. W takim przypadku nakarmiłbym model z metadanych i / lub udekorowałem go.
źródło
Świetne pytanie!
Jeśli chodzi o rozwój sieci na całym świecie, co jeśli zapytasz również o następujące rzeczy.
„Jeśli dane wejściowe złego użytkownika są dostarczane do kontrolera z interfejsu użytkownika, czy sterownik powinien aktualizować widok w rodzaju cyklicznej pętli, wymuszając dokładność poleceń i danych wejściowych przed ich przetworzeniem ? W jaki sposób? Jak widok jest aktualizowany w normalny sposób? warunki? Czy widok jest ściśle powiązany z modelem? Czy podstawowa logika biznesowa walidacji danych wejściowych wprowadzana przez użytkownika , czy też jest ona wstępna i dlatego powinna mieć miejsce w kontrolerze (ponieważ dane wejściowe użytkownika są częścią żądania)?
(Czy w efekcie można i należy opóźnić utworzenie modelu, dopóki nie uzyska się dobrych danych wejściowych?)
Moim zdaniem modele powinny radzić sobie z czystą i nieskazitelną okolicznością (na tyle, na ile to możliwe), nieobciążoną podstawową weryfikacją danych wejściowych żądania HTTP, która powinna nastąpić przed utworzeniem instancji modelu (i na pewno zanim model otrzyma dane wejściowe). Ponieważ zarządzanie danymi stanu (trwałymi lub innymi) i relacjami API jest światem modelu, pozwól, aby podstawowe sprawdzanie poprawności danych wejściowych żądania HTTP miało miejsce w kontrolerze.
Zreasumowanie.
1) Sprawdź poprawność trasy (parsowane z adresu URL), ponieważ kontroler i metoda muszą istnieć, zanim cokolwiek innego będzie mogło być kontynuowane. Zdecydowanie powinno się to zdarzyć w dziedzinie kontrolera frontowego (klasa routera), zanim dojdzie do prawdziwego kontrolera. Duh. :-)
2) Model może mieć wiele źródeł danych wejściowych: żądanie HTTP, bazę danych, plik, interfejs API i tak, sieć. Jeśli zamierzasz umieścić całą weryfikację danych wejściowych w modelu, wówczas uznajesz, że weryfikacja danych wejściowych żądania HTTP stanowi część wymagań biznesowych dla programu. Sprawa zamknięta.
3) Jednak krótkowzroczny jest koszt tworzenia instancji wielu obiektów, jeśli wprowadzanie żądania HTTP nie jest dobre! Możesz wiedzieć, czy ** wejście żądania HTTP ** jest dobre ( które przychodziło z żądaniem ), sprawdzając je przed utworzeniem instancji modelu i wszystkich jego złożoności (tak, być może jeszcze więcej walidatorów dla danych wejściowych / wyjściowych API i DB).
Przetestuj następujące:
a) Metoda żądania HTTP (GET, POST, PUT, PATCH, DELETE ...)
b) Minimalna kontrola HTML (czy masz dość?).
c) Maksymalna kontrola HTML (czy masz ich zbyt wiele?).
d) Prawidłowe formanty HTML (czy masz odpowiednie?).
e) Kodowanie wejściowe (zazwyczaj jest to kodowanie UTF-8?).
f) Maksymalny rozmiar wejściowy (czy którykolwiek z danych wejściowych jest całkowicie poza zakresem?).
Pamiętaj, że możesz otrzymać ciągi i pliki, więc oczekiwanie na utworzenie modelu może być bardzo kosztowne, gdy żądania trafią na twój serwer.
To, co tu opisałem, uderza w intencji żądania przychodzącego przez kontroler. Pominięcie weryfikacji zamiaru pozostawia twoją aplikację bardziej narażoną. Cel może być dobry (gra według twoich podstawowych zasad) lub zły (wykraczanie poza twoje podstawowe zasady).
Zamiarem żądania HTTP jest propozycja „wszystko albo nic”. Wszystko mija lub żądanie jest nieprawidłowe . Nie musisz niczego wysyłać do modelu.
Ten podstawowy poziom żądania HTTP intencją ma nic wspólnego z regularnych błędów wejściowych użytkownika i walidacji. W moich aplikacjach żądanie HTTP musi być ważne na pięć powyższych sposobów, aby je honorować. Mówiąc bardziej szczegółowo o obronie , nigdy nie przejdziesz do sprawdzania poprawności danych wejściowych przez użytkownika po stronie serwera, jeśli którakolwiek z tych pięciu rzeczy zawiedzie.
Tak, oznacza to, że nawet dane wejściowe pliku muszą być zgodne z twoimi frontonowymi próbami weryfikacji i poinformowania użytkownika o maksymalnym zaakceptowanym rozmiarze pliku. Tylko HTML? Brak JavaScript? W porządku, ale użytkownik musi zostać poinformowany o konsekwencjach przesyłania plików, które są zbyt duże (przede wszystkim, że stracą wszystkie dane formularza i zostaną wyrzuceni z systemu).
4) Czy to oznacza, że dane wejściowe żądania HTTP nie są częścią logiki biznesowej aplikacji? Nie, oznacza to po prostu, że komputery są urządzeniami skończonymi, a zasoby muszą być mądrze wykorzystywane. Sensowne jest zatrzymanie złośliwej aktywności wcześniej, a nie później. Płacisz więcej za zasoby obliczeniowe za czekanie, aby zatrzymać je później.
5) Jeśli dane wejściowe żądania HTTP są złe, całe żądanie jest złe . Tak na to patrzę. Definicja dobrych danych wejściowych żądania HTTP pochodzi z wymagań biznesowych modelu, ale musi istnieć pewien punkt rozgraniczenia zasobów. Jak długo pozwolisz żyć złej prośbie, zanim ją zabijesz i powiesz: „Och, hej, nieważne. Zła prośba”.
Wyrok nie polega po prostu na tym, że użytkownik popełnił rozsądny błąd przy wprowadzaniu danych, ale że żądanie HTTP jest tak poza zasięgiem, że musi zostać uznane za złośliwe i natychmiast zatrzymane.
6) Tak więc, dla moich pieniędzy, żądanie HTTP (METODA, adres URL / trasa i dane) jest WSZYSTKIE dobre, albo NIC nie może kontynuować. Solidny model ma już zadania sprawdzania poprawności, ale dobry pasterz zasobów mówi: „Moja droga lub wysoka droga. Przyjdź dobrze, albo wcale nie przychodź”.
Ale to twój program. „Jest na to więcej niż jeden sposób”. Niektóre sposoby kosztują więcej czasu i pieniędzy niż inne. Sprawdzanie poprawności danych żądań HTTP później (w modelu) powinno kosztować więcej w całym okresie użytkowania aplikacji (szczególnie w przypadku skalowania w górę lub w dół).
Jeśli walidatory są modułowe, sprawdzanie poprawności * podstawowych danych wejściowych żądań HTTP ** w kontrolerze nie powinno stanowić problemu. Wystarczy użyć strategicznej klasy Validator, w której walidatory czasami składają się również ze specjalistycznych walidatorów (e-mail, telefon, token formularza, captcha, ...).
Niektórzy uważają to za całkowicie błędne, ale HTTP był w powijakach, gdy Gang of Four napisał Design Patterns: Elements of Re-useable Object-Oriented Software .
================================================== ========================
Teraz, ponieważ dotyczy normalnego sprawdzania poprawności danych wprowadzanych przez użytkownika (po tym, jak żądanie HTTP zostało uznane za prawidłowe), aktualizuje widok, gdy użytkownik się zorientuje, o czym należy pomyśleć! Ten rodzaj sprawdzania poprawności danych wejściowych przez użytkownika powinien mieć miejsce w modelu.
Nie masz gwarancji JavaScript w interfejsie. Oznacza to, że nie ma możliwości zagwarantowania asynchronicznej aktualizacji interfejsu użytkownika aplikacji o statusie błędu. Prawdziwe progresywne ulepszenie obejmowałoby również przypadek użycia synchronicznego.
Uwzględnianie przypadku użycia synchronicznego to sztuka, która jest coraz bardziej zagubiona, ponieważ niektórzy ludzie nie chcą tracić czasu i problemów związanych ze śledzeniem stanu wszystkich swoich sztuczek interfejsu użytkownika (pokaż / ukryj elementy sterujące, wyłącz / włącz elementy sterujące , wskazania błędów, komunikaty o błędach) na zapleczu (zwykle poprzez śledzenie stanu w tablicach).
Aktualizacja : Na schemacie mówię, że
View
powinien odnosić się doModel
. Nie. Powinieneś przekazać daneView
z,Model
aby zachować luźne połączenie.źródło