0 follower

יצירת מודל

לפני כתיבת קוד ה HTML הנחוץ לטופס, אנו צריכים להחליט אילו נתונים אנו מצפים לקבל ממשתמשי הקצה ואילו חוקים אותם נתונים צריכים לעמוד בהם. ניתן להעזר במודל בישביל לתעד מידע זה. מודל, כפי שהוגדר בחלק לגבי מודלים, הוא המקום המרכזי עבור שמירת נתונים ואימות הנתונים המגיעים מהמשתמשים.

תלוי באופן השימוש שלנו בנתונים המגיעים מהמשתמשים, אנו יכולים ליצור שני סוגים של מודלים. אם המידע שהמשתמש מזין נאסף, משומש ואז נזרק, אנו ניצור מודל טופס; אם המידע של המשתמש נאסף ונשמר במסד הנתונים, אנו ניצור מודל AR במקום. שני המחלקות יורשות מהמחלקה הבסיסית CModel שמגדירים את הממשק הכללי הדרוש בטופס.

» Note|הערה: בדוגמאות בחלק זה אנו בעיקר משתמשים במודל טופס. למרות, שניתן להשתמש באותם דוגמאות על גבי מודל AR.

1. הגדרת מחלקת מודל

למטה אנו יוצרים מחלקת מודל בשם LoginForm שאנו נעזרים בה לאיסוף מידע שמשתמש מזין בעמוד ההתחברות. מאחר והמידע של ההתחברות נועד למטרת אימות בלבד ואין צורך לשמור אותו, אנו יוצרים את ה LoginForm כמודל טופס.

class LoginForm extends CFormModel
{
    public $username;
    public $password;
    public $rememberMe=false;
}

שלושה משתנים מוגדרים ב LoginForm: המשתנים המוגדרים הם username$, password$ ו rememberMe$. הם נועדו כדי לאפשר למשתמש להזין שם משתמש וסיסמא, ואפשרות לבחור במידה והוא רוצה לשמור את ההתחברות שלו לתקופה ממושכת. מאחר ולמשתנה rememberMe$ ישנו ערך ברירת מחדל של false, כפתור הבחירה של המשתנה הנלמד יהיה לא מסומן כברירת מחדל בטופס.

» Info|מידע: במקום לקרוא למשתנים הללו מאפיינים, אנו משתמשים במונח משתנים בכדי להבדיל אותם ממאפיינים רגילים. מאפיינים נועדו בעיקר לשמירת מידע המגיע מהמשתמש.

2. הגדרת חוקי אימות נתונים

ברגע שהמשתמש שולח את הנתונים שהוא הזין והמודל מאוכלס בנתונים אלו, אנו צריכים לוודא שהנתונים תקינים לפני שאנו משתמשים בהם. זה נעשה על ידי ביצוע אימות נתונים על גבי סט של חוקים. אנו מגדירים את חוקי אימות הנתונים במתודה ()rules שצריכה להחזיר מערך של הגדרות.

class LoginForm extends CFormModel
{
    public $username;
    public $password;
    public $rememberMe=false;
 
    private $_identity;
 
    public function rules()
    {
        return array(
            array('username, password', 'required'),
            array('rememberMe', 'boolean'),
            array('password', 'authenticate'),
        );
    }
 
    public function authenticate($attribute,$params)
    {
        $this-»_identity=new UserIdentity($this-»username,$this-»password);
        if(!$this-»_identity-»authenticate())
            $this-»addError('password','Incorrect username or password.');
    }
}

הקוד למעלה מציין ש username ו password שניהם הם שדות חובה, password צריך להיות מאומת, ו rememberMe צריך להיות ערך בוליאני (boolean).

כל חוק הנמצא במתודה ()rules צריך להיות מוגדר בפורמט הבא:

array('AttributeList', 'Validator', 'on'=»'ScenarioList', ...additional options)

כשהערך AttributeList הינו סטרינג המופרד בפסיקים של שמות שצריך לאמת אותם מול החוק שהוגדר; Validator מגדיר את סוג הבדיקה שיש לבצע על גבי המשתנים שהוגדרו במשתנה קודם לכן; on הינו ערך אופציונלי המגדיר באילו תסריטים (מצבים) יש לבצע את האימות הנוכחי; ואפשרויות נוספות הינם זוגות של מפתח=»ערך שנועדו לאתחול של המאפיינים בחוק הנוכחי.

ישנם שלושה דרכים להגדרת Validator בחוק. אפשרות ראשונה, Validator יכול להיות שמה של מתודה במחלקת המודל הנוכחי, כמו authenticate בדוגמא למעלה. המתודה צריכה להראות בצורה הבאה:

/**
 * @param string the name of the attribute to be validated
 * @param array options specified in the validation rule
 */
public function ValidatorName($attribute,$params) { ... }

אפשרות שנייה, Validator יכול להיות שם של מחלקת אימות נתונים. כשהחוק יצורף, יווצר אובייקט של המחלקה אשר יבצע את הבדיקה. האפשרויות הנוספות בהגדרת החוק (כפי שתואר למעלה) נועדו כדי להגדיר את מאפייני המחלקה בעת יצירת האובייקט של המחלקה. מחלקת אימות נתונים צריכה להיות תת מחלקה של CValidator.

» Note|הערה: ברגע שמגדירים חוקים למודל AR, אנו יכולים להשתמש באפשרות מיוחדת בשם on. האפשרות יכולה להיות insert או update כדי שהחוק המדובר יצורף רק כשמוסיפים או מעדכנים רשומה, בהתאמה. במידה ולא הוגדר, החוק יצורף בשני המקרים בעת הקריאה ל ()save.

אפשרות שלישית, Validator יכול להוות שם מקוצר למחלקות אימות נתונים שהוגדרו מראש. בדוגמא למעלה, השם required הינו שם מקוצר למחלקה CRequiredValidator אשר מוודא שהערך של המשתנה הנבדק אינו ריק. למטה מוצגת הרשימה המלאה לשמות מקוצרים למחלקות בדיקות נתונים המוגדרות מראש:

  • boolean: שם מקוצר עבור המחלקה CBooleanValidator, מוודא שהמשתנה מכיל ער של CBooleanValidator::trueValue או CBooleanValidator::falseValue.

  • captcha: שם מקוצר עבור המחלקה CCaptchaValidator, המוודא שהערך שהוזן זהה לערך שהוצג על ידי CAPTCHA.

  • compare: שם מקוצר עבור המחלקה CCompareValidator, מוודא שהערך שנבדק זהה לערך הנוסף שהוגדר.

  • email: שם מקוצר עבור המחלקה CEmailValidator, מוודא שהערך הנבדק הינו אימייל תקני.

  • default: שם מקוצר עבור המחלקה CDefaultValueValidator, מגדיר ערכים ברירת מחדל עבור המשתנים הנבדקים.

  • exist: שם מקוצר עבור המחלקה CExistValidator, מוודא שהערך הנבדק קיים בעמודה בטבלה הנוכחית.

  • file: שם מקוצר עבור המחלקה CFileValidator, מוודא שהערך הנבדק מכיל את שם הקובץ שהועלה.

  • filter: שם מקוצר עבור המחלקה CFilterValidator, המרת הערך הנבדק דרך פילטר שהוגדר.

  • in: שם מקוצר עבור המחלקה CRangeValidator, מוודא שהערך הנבדק הוא בין רשימת הערכים שהוגדרו.

  • length: שם מקוצר עבור המחלקה CStringValidator, מוודא שאורך הערך הנבדק הוא באורך מסויים.

  • match: שם מקוצר עבור המחלקה CRegularExpressionValidator, מוודא שהערך תואם לביטוי רגולרי.

  • numerical: שם מקוצר עבור המחלקה CNumberValidator, מוודא שהערך הנבדק הוא מספר.

  • required: שם מקוצר עבור המחלקה CRequiredValidator, מוודא שהערך הנבדק אינו ריק.

  • type: שם מקוצר עבור המחלקה CTypeValidator, מוודא שהערך הנבדק הוא מסוג מסויים.

  • unique: שם מקוצר עבור המחלקה CUniqueValidator, מוודא שהערך הינו יחודי בעמודה בטבלה.

  • url: שם מקוצר עבור המחלקה CUrlValidator, מוודא שהערך הנבדק הינו קישור תקני.

למטה מוצגים כמה דוגמאות לשימוש במחלקות אימות נתונים המוגדרים מראש:

// שם משתמש הוא הכרחי
array('username', 'required'),
// שם משתמש חייב להיות בין 3 ל 12 תווים
array('username', 'length', 'min'=»3, 'max'=»12),
// בזמן הרשמה הסיסמא צריכה להיות תואמת לאישור הסיסמא
array('password', 'compare', 'compareAttribute'=»'password2', 'on'=»'register'),
// בזמן התחברות הסיסמא צריכה להיות מאומתת
array('password', 'authenticate', 'on'=»'login'),

3. אבטחת הצבת מאפיינים

לאחר יצירת אובייקט של מודל, אנו בדרך כלל צריכים לאכלס את המאפיינים שלו על ידי המידע שהמשתמש הזין. ניתן לבצע זאת בצורה פשוטה ונוחה על ידי הצבה מאסיבית של מאפיינים:

$model=new LoginForm;
if(isset($_POST['LoginForm']))
    $model-»attributes=$_POST['LoginForm'];

הפקודה האחרונה נקראת הצבה מאסיבית אשר מציבה את כל המאפיינים ב $_POST['LoginForm'] למאפיינים המתאימים במודל. פקודה זו זהה לקוד הבא:

foreach($_POST['LoginForm'] as $name=»$value)
{
    if($name is a safe attribute)
        $model-»$name=$value;
}

זה הכרחי להגדיר אילו מאפיינים הם מאובטחים. לדוגמא, במידה ונחשוף את העמודה השומרת את המפתח הראשי בטבלה ונגדיר אותו להיות מאפיין מאובטח, ניתן יהיה לשנות את המפתח הראשי של מידע מסויים ובכך לקבל גישה לנתונים שמשתמשים לא צריכים לראות.

התנאים בהגדרת מאפיינים בטוחים שונים בין הגרסאות 1.0 ו 1.1. בחלק הבא, אנו נסביר לגביהם בנפרד.

מאפיינים בטוחים ב 1.1

בגרסא 1.1, מאפיין מוגדר כמאובטח אם הוא מופיע בחוקים לאימות הנתונים המוגדרים לעבוד עם התסריט הנוכחי. לדוגמא,

array('username, password', 'required', 'on'=»'login, register'),
array('email', 'required', 'on'=»'register'),

בקוד למעלה, המאפיינים username ו password הם שדו חובה בתסריט של login, בזמן שהמאפיינים username, password ו email הם שדות חובה בתסריט של register. כתוצאה מכך, אם נבצע הצבה מאסיבית של נתונים למאפיינים כשאנו נמצאים בתסריט של login, רק המאפיינים username ו password יוצבו בצורה מאסיבית מאחר ורק מאפיינים אלו מופיעים בחוקים עבור התסריט login. מצד שני, אם התסריט הוא register, כל שלושת המאפיינים יוצבו בצורה מאסיבית.

// בתסריט התחברות
$model=new User('login');
if(isset($_POST['User']))
    $model-»attributes=$_POST['User'];
 
// בתסריט הרשמה
$model=new User('register');
if(isset($_POST['User']))
    $model-»attributes=$_POST['User'];

אז למה אנחנו משתמשים במדיניות זו בכדי לקבוע אם מאפיין הוא בטוח או לא? הלוגיקה מאחורי זה היא אם מאפיין כבר מוגדר עם חוק אחד או יותר לאימות הנתונים בו, למה לנו לדאוג בנוגע אליו?

חשוב לזכור שחוקי אימות הנתונים נועדו לאמת את הנתונים המגיעים מהמשתמש ולא את הנתונים שאנו יוצרים בקוד (לדוגמא זמן נוכחי, ערך המפתח הראשי שנוצר אוטומטית). לכן, אין להוסיף חוקים לאימות הנתונים לנתונים שלא מגיעים מהמשתמשים.

לפעמים, אנחנו מגדירים מאפיין בטוח, למרות שאין לנו חוק מסויים עבורו. לדוגמא מאפיין של תוכן מאמר שיכול להכיל כל תוכן שהמשתמש מזין. אנו יכולים להשתמש בחוק מיוחד בשם safe בכדי להשיג מטרה זו:

array('content', 'safe')

לצורך השלמת הקוד, ישנו חוק בשם unsafe שנועד להגדיר מאפיין כלא בטוח ולא להכליל אותו בנתונים:

array('permission', 'unsafe')

בדרך כלל אין שימוש בחוק unsafe, והוא ההפך ממה שדובר לגבי חוק ה safe עבור מאפיינים בטוחים.

מאפיינים בטוחים ב 1.0

בגרסאות 1.0, ההחלטה בין אם מאפיין הוא בטוח או לא מבוססת על הערך המוחזר מהמתודה safeAttributes והתסריט המוגדר כרגע. כברירת מחדל, המתודה מחזירה את כל מאפייני המחלקה CFormModel כמאפיינים בטוחים, בזמן שהינה מחזירה את כל העמודות בטבלה מלבד העמודה של המפתח הראשי עבור המחלקה CActiveRecord. אנו יכולים לדרוס מתודה זו בכדי להגביל את המאפיינים הבטוחים לפי התסריט המוגדר. לדוגמא, מודל משתמשים יכול להכיל מאפיינים רבים, אבל בזמן השימוש בתסריט login אנו נצטרך רק את המאפיינים של שם המשתמש והסיסמא, username ו password. אנו יכולים להגדיר את ההגבלה בצורה הבאה:

public function safeAttributes()
{
    return array(
        parent::safeAttributes(),
        'login' =» 'username, password',
    );
}

יותר מדוייק, הערך המוחזר מהמתודה safeAttributes צריך להראות כמו התבנית הבאה:

array(
   // מאפיינים אלו ניתנים להצבה מאסיבית בכל תסריט
   // שלא הוגדר ספציפית כמו בדוגמאות למטה
   'attr1, attr2, ...',
     *
   // מאפיינים אלו ניתנים להצבה מאסיבית רק בזמן השימוש בתסריט מספר 1
   'scenario1' =» 'attr2, attr3, ...',
     *
   // מאפיינים אלו ניתנים להצבה מאסיבית רק בזמן השימוש בתסריט מספר 2
   'scenario2' =» 'attr1, attr3, ...',
)

במידה והמודל אינו רגיש כלפי תסריט (כלומר, משתמשים בו עם תסריט אחד בלבד, או כל התסריטים משתמשים באותם המאפיינים הבטוחים), ניתן לפשט את הערך המוחזר בסטרינג בודד:

'attr1, attr2, ...'

עבור מאפיינים שהם לא בטוחים, אנו צריכים להגדיר אותם בצורה פרטנית בהתאם לערכים שהם אמורים להכיל, לדוגמא,

$model-»permission='admin';
$model-»id=1;

4. ביצוע אימות הנתונים

לאחר שהמודל מאוכלס עם הנתונים שהמשתמש הזין, אנו קוראים למתודה [()CModel::validate] בכדי להריץ את תהליך אימות הנתונים. המתודה מחזירה ערך המציין אם תהליך אימות הנתונים עבר בהצלחה או לא. עבור מודלים של CActiveRecord, אימות הנתונים יתבצע בצורה אוטומטית ברגע שנקרא למתודה [()CActiveRecord::save].

אנו יכולים לציין את התסריט הנוכחי על ידי הגדרת המאפיין scenario ועל ידי כך לציין אילו חוקים יצורפו לבדיקה זו.

אימות הנתונים מתבצע על בסיס התסריט. המאפיין scenario מגדיר איזה תסריט המודל כרגע משתמש בו ובאילו חוקים יש להשתמש באימות הנתונים כרגע. לדוגמא, בתסריט של login, אנו נרצה רק לאמת את שם המשתמש והסיסמא - username ו password הנמצאים במודל של המשתמשים; בזמן שבתסריט register, אנו נצטרך לאמת יותר נתונים, כמו email, address, וכדומה. הדוגמא הבאה מציגה כיצד לבצע אימות נתונים תחת תסריט register:

// יוצר מודל כשמצב התסריט מוגדר ל `register`. הקוד זהה ל:
// $model=new User;
// $model-»scenario='register';
$model=new User('register');
 
// מאכלס את הנתונים שהגיע מהמשתמש אל המודל
$model-»attributes=$_POST['User'];
 
// מבצע את הבדיקה
if($model-»validate())   // במידה והנתונים תקינים
    ...
else
    ...

בכדי להגדיר חוקים מסויימים עבור תסריטים מסויימים יש להגדיר את המאפיין on בהגדרה של החוק עצמו. במידה והמאפיין on לא הוגדר, זה אומר שהחוק צריך להיות תקף בכל התסריטים האפשריים. לדוגמא,

public function rules()
{
    return array(
        array('username, password', 'required'),
        array('password_repeat', 'required', 'on'=»'register'),
        array('password', 'compare', 'on'=»'register'),
    );
}

החוק הראשון יצורף לכל התסריטים, בזמן ששני הבאים יצורפו רק לתסריט של register.

5. קבלת שגיאות אימות נתונים

לאחר ביצוע האימות, כל השגיאות הקיימות יאוחסנו תחת האובייקט של המודל. אנו שולפים את הודעות השגיאות על ידי קריאה למתודה [()CModel::getErrors] או [()CModel::getError]. ההבדל בין השניים הוא שהראשון יחזיר את כל השגיאות עבור מאפיין כלשהו במודל. בזמן שהשני מחזיר את השגיאה הראשונה עבור מאפיין כלשהו.

6. תוויות מאפיינים

בעת עיצוב טופס, אנחנו בדרך כלל צריכים להציג תווית עבור כל שדה. התוית אומרת למשתמש איזה סוג מידע הוא צריך להזין לשדה. למרות שאנו יכולים להוסיף זאת ישירות בתוך קוד ה HTML, יש עדיפות להגדיר את התויות של השדות במחלקה של המודל בכדי לקבל יותר גמישות ולנוחות השימוש.

כברירת מחדל, CModel יפשט את החזרת השם של מאפיין כלשהו המוגדר בו. ניתן להגדיר זאת על ידי דריסה של המתודה ()attributeLabels. כפי שאנו נראה בחלקים הבאים, הגדרת התויות של מאפיינים במודל יאפשר לנו ליצור טופס בצורה הרבה יותר מהירה ויעילה.

Found a typo or you think this page needs improvement?
Edit it on github !