Partager via


Examen des méthodes d’action et des vues pour le contrôleur de film

par Rick Anderson

Remarque

Une version mise à jour de ce didacticiel est disponible ici qui utilise ASP.NET MVC 5 et Visual Studio 2013. Il est plus sécurisé, beaucoup plus simple à suivre et montre plus de fonctionnalités.

Dans cette section, vous allez examiner les méthodes et vues d’action générées pour le contrôleur de film. Ensuite, vous allez ajouter une page de recherche personnalisée.

Exécutez l’application et accédez au Movies contrôleur en ajoutant /Movies à l’URL dans la barre d’adresse de votre navigateur. Maintenez le pointeur de la souris sur un lien Modifier pour afficher l’URL vers laquelle il est lié.

EditLink_sm

Le lien Edit a été généré par la Html.ActionLink méthode dans la vue Views\Movies\Index.cshtml :

@Html.ActionLink("Edit", "Edit", new { id=item.ID })

Html.ActionLink

L’objet Html est un assistance exposé à l’aide d’une propriété sur la classe de base System.Web.Mvc.WebViewPage . La ActionLink méthode de l’assistance facilite la génération dynamique de liens hypertexte HTML qui relient les méthodes d’action sur les contrôleurs. Le premier argument de la ActionLink méthode est le texte du lien à afficher (par exemple). <a>Edit Me</a> Le deuxième argument est le nom de la méthode d’action à appeler. L’argument final est un objet anonyme qui génère les données de routage (dans ce cas, l’ID de 4).

Le lien généré affiché dans l’image précédente est http://localhost:xxxxx/Movies/Edit/4. L’itinéraire par défaut (établi dans App_Start\RouteConfig.cs) prend le modèle {controller}/{action}/{id}d’URL. Par conséquent, ASP.NET se traduit http://localhost:xxxxx/Movies/Edit/4 en une requête en méthode Edit d’action du Movies contrôleur avec le paramètre ID égal à 4. Examinez le code suivant à partir du fichier App_Start\RouteConfig.cs .

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", 
            id = UrlParameter.Optional }
    );
}

Vous pouvez également passer des paramètres de méthode d’action à l’aide d’une chaîne de requête. Par exemple, l’URL http://localhost:xxxxx/Movies/Edit?ID=4 transmet également le paramètre ID 4 à la Edit méthode d’action du Movies contrôleur.

EditQueryString

Ouvrez le Movies contrôleur. Les deux Edit méthodes d’action sont indiquées ci-dessous.

//
// GET: /Movies/Edit/5

public ActionResult Edit(int id = 0)
{
    Movie movie = db.Movies.Find(id);
    if (movie == null)
    {
        return HttpNotFound();
    }
    return View(movie);
}

//
// POST: /Movies/Edit/5

[HttpPost]
public ActionResult Edit(Movie movie)
{
    if (ModelState.IsValid)
    {
        db.Entry(movie).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(movie);
}

Notez que la deuxième méthode d’action Edit est précédée de l’attribut HttpPost. Cet attribut spécifie que la surcharge de la Edit méthode peut être appelée uniquement pour les requêtes POST. Vous pouvez appliquer l’attribut HttpGet à la première méthode d’édition, mais cela n’est pas nécessaire, car il s’agit de la valeur par défaut. (Nous allons faire référence à des méthodes d’action qui sont implicitement affectées à l’attribut HttpGet en tant que HttpGet méthodes.)

La HttpGet Edit méthode prend le paramètre ID de film, recherche le film à l’aide de la méthode Entity Framework Find et retourne le film sélectionné en mode Édition. Le paramètre ID spécifie une valeur par défaut de zéro si la Edit méthode est appelée sans paramètre. Si un film est introuvable, HttpNotFound est retourné. Quand le système de génération de modèles automatique a créé la vue Edit, il a examiné la classe Movie et a créé le code pour restituer les éléments <label> et <input> de chaque propriété de la classe. L’exemple suivant montre l’affichage Modifier qui a été généré :

@model MvcMovie.Models.Movie

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Movie</legend>

        @Html.HiddenFor(model => model.ID)

        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.ReleaseDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.ReleaseDate)
            @Html.ValidationMessageFor(model => model.ReleaseDate)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Genre)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Genre)
            @Html.ValidationMessageFor(model => model.Genre)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Price)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Price)
            @Html.ValidationMessageFor(model => model.Price)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Notez que le modèle d’affichage a une @model MvcMovie.Models.Movie instruction en haut du fichier . Cela spécifie que la vue s’attend à ce que le modèle de vue soit de type Movie.

Le code généré automatiquement utilise plusieurs méthodes d’assistance pour simplifier le balisage HTML. L’assistance Html.LabelFor affiche le nom du champ (« Title », « ReleaseDate », « Genre » ou « Price »). L’assistance Html.EditorFor restitue un élément HTML <input> . L’assistance Html.ValidationMessageFor affiche les messages de validation associés à cette propriété.

Exécutez l’application et accédez à l’URL /Movies . Cliquez sur un lien Edit. Dans le navigateur, affichez la source de la page. Le code HTML de l’élément de formulaire est illustré ci-dessous.

<form action="/Movies/Edit/4" method="post">    <fieldset>
        <legend>Movie</legend>

        <input data-val="true" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="4" />

        <div class="editor-label">
            <label for="Title">Title</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" id="Title" name="Title" type="text" value="Rio Bravo" />
            <span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>
        </div>

        <div class="editor-label">
            <label for="ReleaseDate">ReleaseDate</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" data-val="true" data-val-date="The field ReleaseDate must be a date." data-val-required="The ReleaseDate field is required." id="ReleaseDate" name="ReleaseDate" type="text" value="4/15/1959 12:00:00 AM" />
            <span class="field-validation-valid" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
        </div>

        <div class="editor-label">
            <label for="Genre">Genre</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" id="Genre" name="Genre" type="text" value="Western" />
            <span class="field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
        </div>

        <div class="editor-label">
            <label for="Price">Price</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number." data-val-required="The Price field is required." id="Price" name="Price" type="text" value="2.99" />
            <span class="field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
</form>

Les <input> éléments se trouvent dans un élément HTML <form> dont action l’attribut est défini pour publier sur l’URL /Movies/Edit . Les données de formulaire sont publiées sur le serveur lorsque le bouton Modifier est cliqué.

Traitement de la requête POST

Le code suivant montre la version HttpPost de la méthode d’action Edit.

[HttpPost] 
public ActionResult Edit(Movie movie)  
{ 
    if (ModelState.IsValid)  
    { 
        db.Entry(movie).State = EntityState.Modified; 
        db.SaveChanges(); 
        return RedirectToAction("Index"); 
    } 
    return View(movie); 
}

Le classeur de modèles MVC ASP.NET prend les valeurs de formulaire publiées et crée un Movie objet passé en tant que movie paramètre. La méthode ModelState.IsValid vérifie que les données envoyées dans le formulaire peuvent être utilisées pour changer (modifier ou mettre à jour) un objet Movie. Si les données sont valides, les données de film sont enregistrées dans la Movies collection de l’instance db(MovieDBContext ). Les nouvelles données de film sont enregistrées dans la base de données en appelant la SaveChanges méthode de MovieDBContext. Après avoir enregistré les données, le code redirige l’utilisateur vers la Index méthode d’action de la MoviesController classe, qui affiche la collection de films, y compris les modifications apportées.

Si les valeurs publiées ne sont pas valides, elles sont réaffichées dans le formulaire. Les Html.ValidationMessageFor helpers du modèle d’affichage Edit.cshtml s’occupent de l’affichage des messages d’erreur appropriés.

abcNotValid

Remarque

pour prendre en charge la validation jQuery pour les paramètres régionaux non anglais qui utilisent une virgule (« , ») pour un point décimal, vous devez inclure des globalize.js et vos cultures/fichiers globalize.cultures.js spécifiques (à partir de https://github.com/jquery/globalize ) et JavaScript à utiliserGlobalize.parseFloat. Le code suivant montre les modifications apportées au fichier Views\Movies\Edit.cshtml pour utiliser la culture « fr-FR » :

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    <script src="~/Scripts/globalize.js"></script>
    <script src="~/Scripts/globalize.culture.fr-FR.js"></script>
    <script>
        $.validator.methods.number = function (value, element) {
            return this.optional(element) ||
                !isNaN(Globalize.parseFloat(value));
        }
        $(document).ready(function () {
            Globalize.culture('fr-FR');
        });
    </script>
    <script>
        jQuery.extend(jQuery.validator.methods, {    
            range: function (value, element, param) {        
                //Use the Globalization plugin to parse the value        
                var val = $.global.parseFloat(value);
                return this.optional(element) || (
                    val >= param[0] && val <= param[1]);
            }
        });

    </script>
}

Le champ décimal peut nécessiter une virgule, et non un point décimal. En guise de correctif temporaire, vous pouvez ajouter l’élément de globalisation au fichier web.config racine des projets. Le code suivant montre l’élément de globalisation avec la culture définie sur États-Unis l’anglais.

<system.web>
    <globalization culture ="en-US" />
    <!--elements removed for clarity-->
  </system.web>

Toutes les HttpGet méthodes suivent un modèle similaire. Ils obtiennent un objet vidéo (ou liste d’objets, dans le cas de Index), et passent le modèle à la vue. La Create méthode transmet un objet vidéo vide à l’affichage Create. Toutes les méthodes qui créent, modifient, suppriment ou changent d’une quelconque manière des données le font dans la surcharge HttpPost de la méthode. La modification de données dans une méthode HTTP GET est un risque de sécurité. La modification de données dans une méthode GET enfreint également les meilleures pratiques HTTP et le modèle REST architectural, qui spécifie que les requêtes GET ne doivent pas changer l’état de votre application. En d’autres termes, une opération GET doit être sûre, ne doit avoir aucun effet secondaire et ne doit pas modifier vos données persistantes.

Ajout d’une méthode de recherche et d’une vue de recherche

Dans cette section, vous allez ajouter une méthode d’action SearchIndex qui vous permet de rechercher des films par genre ou par nom. Cette opération sera disponible à l’aide de l’URL /Movies/SearchIndex . La requête affiche un formulaire HTML qui contient des éléments d’entrée qu’un utilisateur peut entrer pour rechercher un film. Lorsqu’un utilisateur envoie le formulaire, la méthode d’action obtient les valeurs de recherche publiées par l’utilisateur et utilise les valeurs pour rechercher la base de données.

Affichage du formulaire SearchIndex

Commencez par ajouter une méthode d’action SearchIndex à la classe existante MoviesController . La méthode retourne une vue qui contient un formulaire HTML. Voici le code :

public ActionResult SearchIndex(string searchString) 
{           
    var movies = from m in db.Movies 
                 select m; 
 
    if (!String.IsNullOrEmpty(searchString)) 
    { 
        movies = movies.Where(s => s.Title.Contains(searchString)); 
    } 
 
    return View(movies); 
}

La première ligne de la SearchIndex méthode crée la requête LINQ suivante pour sélectionner les films :

var movies = from m in db.Movies 
             select m;

La requête est définie à ce stade, mais n’a pas encore été exécutée sur le magasin de données.

Si le searchString paramètre contient une chaîne, la requête films est modifiée pour filtrer la valeur de la chaîne de recherche à l’aide du code suivant :

if (!String.IsNullOrEmpty(searchString)) 
{ 
    movies = movies.Where(s => s.Title.Contains(searchString)); 
}

Le code s => s.Title ci-dessus est une expression lambda. Les lambda sont utilisés dans les requêtes LINQ basées sur des méthodes de méthode en tant qu’arguments pour les méthodes d’opérateur de requête standard telles que la méthode Where utilisée dans le code ci-dessus. Les requêtes LINQ ne sont pas exécutées lorsqu’elles sont définies ou lorsqu’elles sont modifiées en appelant une méthode telle que Where ou OrderBy. Au lieu de cela, l’exécution de la requête est différée, ce qui signifie que l’évaluation d’une expression est retardée jusqu’à ce que sa valeur réalisée soit réellement itérée ou que la ToList méthode soit appelée. Dans l’exemple SearchIndex , la requête est exécutée dans la vue SearchIndex. Pour plus d’informations sur l’exécution différée des requêtes, consultez Exécution de requête.

Vous pouvez maintenant implémenter l’affichage SearchIndex qui affichera le formulaire à l’utilisateur. Cliquez avec le bouton droit dans la SearchIndex méthode, puis cliquez sur Ajouter un affichage. Dans la boîte de dialogue Ajouter un affichage , spécifiez que vous allez passer un Movie objet au modèle d’affichage en tant que classe de modèle. Dans la liste des modèles de structure Scaffold , choisissez Liste, puis cliquez sur Ajouter.

AddSearchView

Lorsque vous cliquez sur le bouton Ajouter , le modèle de vue Views\Movies\SearchIndex.cshtml est créé. Étant donné que vous avez sélectionné Liste dans la liste des modèles de structure , Visual Studio a généré automatiquement (généré automatiquement) un balisage par défaut dans l’affichage. La structure a créé un formulaire HTML. Il a examiné la classe et créé du Movie code pour afficher <label> les éléments pour chaque propriété de la classe. La liste ci-dessous montre la vue Créer qui a été générée :

@model IEnumerable<MvcMovie.Models.Movie> 
 
@{ 
    ViewBag.Title = "SearchIndex"; 
} 
 
<h2>SearchIndex</h2> 
 
<p> 
    @Html.ActionLink("Create New", "Create") 
</p> 
<table> 
    <tr> 
        <th> 
            Title 
        </th> 
        <th> 
            ReleaseDate 
        </th> 
        <th> 
            Genre 
        </th> 
        <th> 
            Price 
        </th> 
        <th></th> 
    </tr> 
 
@foreach (var item in Model) { 
    <tr> 
        <td> 
            @Html.DisplayFor(modelItem => item.Title) 
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.ReleaseDate) 
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.Genre) 
        </td> 
        <td> 
            @Html.DisplayFor(modelItem => item.Price) 
        </td> 
        <td> 
            @Html.ActionLink("Edit", "Edit", new { id=item.ID }) | 
            @Html.ActionLink("Details", "Details", new { id=item.ID }) | 
            @Html.ActionLink("Delete", "Delete", new { id=item.ID }) 
        </td> 
    </tr> 
} 
 
</table>

Exécutez l’application et accédez à /Movies/SearchIndex. Ajoutez une chaîne de requête comme ?searchString=ghost à l’URL. Les films filtrés sont affichés.

SearchQryStr

Si vous modifiez la signature de la SearchIndex méthode pour avoir un paramètre nommé id, le id paramètre correspond à l’espace {id} réservé pour les itinéraires par défaut définis dans le fichier Global.asax .

{controller}/{action}/{id}

La méthode d’origine SearchIndex ressemble à ceci :

public ActionResult SearchIndex(string searchString) 
{           
    var movies = from m in db.Movies 
                 select m; 
 
    if (!String.IsNullOrEmpty(searchString)) 
    { 
        movies = movies.Where(s => s.Title.Contains(searchString)); 
    } 
 
    return View(movies); 
}

La méthode modifiée SearchIndex se présente comme suit :

public ActionResult SearchIndex(string id) 
{ 
    string searchString = id; 
    var movies = from m in db.Movies 
                 select m; 
 
    if (!String.IsNullOrEmpty(searchString)) 
    { 
        movies = movies.Where(s => s.Title.Contains(searchString)); 
    } 
 
    return View(movies); 
}

Vous pouvez maintenant passer le titre de la recherche en tant que données de routage (un segment de l’URL) et non pas en tant que valeur de chaîne de requête.

SearchRouteData

Cependant, vous ne pouvez pas attendre des utilisateurs qu’ils modifient l’URL à chaque fois qu’ils veulent rechercher un film. Vous allez donc ajouter l’interface utilisateur pour les aider à filtrer les films. Si vous avez modifié la signature de la SearchIndex méthode pour tester comment passer le paramètre ID lié à l’itinéraire, remplacez-le afin que votre SearchIndex méthode accepte un paramètre de chaîne nommé searchString:

public ActionResult SearchIndex(string searchString) 
{           
     var movies = from m in db.Movies 
                  select m; 
 
    if (!String.IsNullOrEmpty(searchString)) 
    { 
        movies = movies.Where(s => s.Title.Contains(searchString)); 
    } 
 
    return View(movies); 
}

Ouvrez le fichier Views\Movies\SearchIndex.cshtml , puis juste après @Html.ActionLink("Create New", "Create"), ajoutez ce qui suit :

@using (Html.BeginForm()){    
         <p> Title: @Html.TextBox("SearchString")<br />  
         <input type="submit" value="Filter" /></p> 
        }

L’exemple suivant montre une partie du fichier Views\Movies\SearchIndex.cshtml avec le balisage de filtrage ajouté.

@model IEnumerable<MvcMovie.Models.Movie> 
 
@{ 
    ViewBag.Title = "SearchIndex"; 
} 
 
<h2>SearchIndex</h2> 
 
<p> 
    @Html.ActionLink("Create New", "Create") 
     
     @using (Html.BeginForm()){    
         <p> Title: @Html.TextBox("SearchString") <br />   
         <input type="submit" value="Filter" /></p> 
        } 
</p>

L’assistance Html.BeginForm crée une balise d’ouverture <form> . L’assistance Html.BeginForm entraîne la publication du formulaire lorsque l’utilisateur envoie le formulaire en cliquant sur le bouton Filtrer .

Exécutez l’application et essayez de rechercher un film.

Capture d’écran de l’exécution de l’application et tentative de recherche d’un film.

Il n’y a pas HttpPost de surcharge de la SearchIndex méthode. Vous n’en avez pas besoin, car la méthode ne change pas l’état de l’application, en filtrant simplement les données.

Vous pourriez ajouter la méthode HttpPost SearchIndex suivante. Dans ce cas, l’appelant d’action correspondrait à la HttpPost SearchIndex méthode et la HttpPost SearchIndex méthode s’exécuterait comme indiqué dans l’image ci-dessous.

[HttpPost] 
public string SearchIndex(FormCollection fc, string searchString) 
{ 
    return "<h3> From [HttpPost]SearchIndex: " + searchString + "</h3>"; 
}

SearchPostGhost

Cependant, même si vous ajoutez cette version HttpPost de la méthode SearchIndex, il existe une limitation dans la façon dont tout ceci a été implémenté. Imaginez que vous voulez insérer un signet pour une recherche spécifique, ou que vous voulez envoyer un lien à vos amis sur lequel ils peuvent cliquer pour afficher la même liste filtrée de films. Notez que l’URL de la requête HTTP POST est identique à l’URL de la requête GET (localhost :xxxxx/Movies/SearchIndex) : il n’existe aucune information de recherche dans l’URL elle-même. À l’heure actuelle, les informations de chaîne de recherche sont envoyées au serveur en tant que valeur de champ de formulaire. Cela signifie que vous ne pouvez pas capturer ces informations de recherche à signet ou à envoyer à des amis dans une URL.

La solution consiste à utiliser une surcharge de BeginForm ce qui spécifie que la requête POST doit ajouter les informations de recherche à l’URL et qu’elle doit être routée vers la version HttpGet de la SearchIndex méthode. Remplacez la méthode sans BeginForm paramètre existante par les éléments suivants :

@using (Html.BeginForm("SearchIndex","Movies",FormMethod.Get))

BeginFormPost_SM

Maintenant, lorsque vous envoyez une recherche, l’URL contient une chaîne de requête de recherche. La recherche accède également à la méthode d’action HttpGet SearchIndex, même si vous avez une méthode HttpPost SearchIndex.

SearchIndexWithGetURL

Ajout de la recherche par genre

Si vous avez ajouté la HttpPost version de la méthode, supprimez-la SearchIndex maintenant.

Ensuite, vous allez ajouter une fonctionnalité pour permettre aux utilisateurs de rechercher des films par genre. Remplacez la méthode SearchIndex par le code suivant :

public ActionResult SearchIndex(string movieGenre, string searchString) 
{ 
    var GenreLst = new List<string>(); 
 
    var GenreQry = from d in db.Movies 
                   orderby d.Genre 
                   select d.Genre; 
    GenreLst.AddRange(GenreQry.Distinct()); 
    ViewBag.movieGenre = new SelectList(GenreLst); 
 
    var movies = from m in db.Movies 
                 select m; 
 
    if (!String.IsNullOrEmpty(searchString)) 
    { 
        movies = movies.Where(s => s.Title.Contains(searchString)); 
    } 
 
    if (string.IsNullOrEmpty(movieGenre)) 
        return View(movies); 
    else 
    { 
        return View(movies.Where(x => x.Genre == movieGenre)); 
    } 
 
}

Cette version de la SearchIndex méthode prend un paramètre supplémentaire, à savoir movieGenre. Les premières lignes de code créent un List objet pour contenir des genres vidéo à partir de la base de données.

Le code suivant est une requête LINQ qui récupère tous les genres dans la base de données.

var GenreQry = from d in db.Movies 
                   orderby d.Genre 
                   select d.Genre;

Le code utilise la AddRange méthode de la collection générique List pour ajouter tous les genres distincts à la liste. (Sans le modificateur, les Distinct genres en double seraient ajoutés , par exemple, la comédie serait ajoutée deux fois dans notre exemple). Le code stocke ensuite la liste des genres dans l’objet ViewBag .

Le code suivant montre comment vérifier le movieGenre paramètre. S’il n’est pas vide, le code limite davantage la requête films pour limiter les films sélectionnés au genre spécifié.

if (string.IsNullOrEmpty(movieGenre)) 
        return View(movies); 
else 
{ 
    return View(movies.Where(x => x.Genre == movieGenre)); 
}

Ajout de balisage à la vue SearchIndex pour prendre en charge la recherche par genre

Ajoutez un Html.DropDownList assistance au fichier Views\Movies\SearchIndex.cshtml , juste avant l’assistance TextBox . Le balisage terminé est illustré ci-dessous :

<p> 
    @Html.ActionLink("Create New", "Create") 
    @using (Html.BeginForm("SearchIndex","Movies",FormMethod.Get)){     
         <p>Genre: @Html.DropDownList("movieGenre", "All")   
           Title: @Html.TextBox("SearchString")   
         <input type="submit" value="Filter" /></p> 
        } 
</p>

Exécutez l’application et accédez à /Movies/SearchIndex. Essayez une recherche par genre, par nom de film, et par les deux critères.

Capture d’écran de l’exécution de l’application et tentative de recherche par nom de film de genre et par critères.

Dans cette section, vous avez examiné les méthodes d’action CRUD et les vues générées par l’infrastructure. Vous avez créé une méthode d’action de recherche et une vue qui permettent aux utilisateurs de rechercher par titre et genre de film. Dans la section suivante, vous allez découvrir comment ajouter une propriété au Movie modèle et comment ajouter un initialiseur qui créera automatiquement une base de données de test.