[MUSIC PLAYING] ERIC OUYANG: Hey, everyone. Welcome. This is Building Dynamic Web Apps with Laravel. My name is Eric Ouyang. I am a sophomore studying social studies with a secondary in computer science, and I live in Adams House here at Harvard. So Laravel, at its most core, is a MVC web app framework. So like what you guys have been doing with CS50 Finance, Laravel is a framework that allows you to build dynamic web apps. So you can think of it in some senses as an extension of the type of stuff you've been doing in CS50 Finance, but a system that's a lot more robust, a lot more elegant in some senses, and provides a lot of functionality for you to build fairly complex web applications. So let's go through some of the key features, and then we'll dive into an example of building a blog application with Laravel. So one of the first things that differentiates it from MVC type of work that you've been doing with CS50 is it includes a ORM engine. So ORM stands for object relational mapping. So this allows you to build a layer of abstraction between the database and your controllers. So unlike in CS50 Finance where you directly make queries, the ORM layer allows you to abstract that away and create models that are more powerful than you can directly with SQL queries. Another thing that's really helpful is inheritable templates. So you'll notice in CS50 Finance, you end up rewriting a lot of things, where places you could potentially reuse things, you're not able to. So here in Laravel, you can use what's known as the blade template engine to create a master layout. And from there, you can inherit so that sub-templates can actually include elements within that larger base template. Migration. So this is a fairly standard features on most modern web application frameworks. So this allows you to represent database schema changes in code. So without going to say, phpMyAdmin, you can actually create these migrations where you represent the database schema changes in code directly. And this allows you to-- especially if you have multiple people working on the same web application-- track these changes, say in GitHub, or some other repository. So this is really helpful and mitigates the need to, say, pass around a lot of SQL dumps. And finally, Composer is something really, really useful that allows you to use other people's code to do awesome things. So Laravel's structured as several composer packages. So say if you wanted to bring in an authentication package or if you wanted to bring in some sort of generator script or an admin interface, you can plug and play those components with Composer. So let's get started. Any questions from you guys before we get started? No questions? Cool. So the first step is installing Composer. So Composer allows you to manage these dependencies, whether it's the Laravel framework or some other third party extension. The first command allows you to download Composer, and the second command allows you to move it into your local bin folder so that you can run Composer directly through the terminal. After that, go ahead and create a new Laravel project. We're actually going to use some example code that I've put together to create this blog. But if you're starting from scratch, you would use this command up here, composer create-project, laravel slash laravel, and then the name of your project. And that will include all the distribution code for starting a new Laravel project. So for your CS50 final projects, you'll probably want to use that command. But we're going to start with this. So once you've done that, you're going to get a fairly extensive number of files in blog50. So let's just go through some of these components. You'll notice at this route directory, there is an app folder. Inside the app folder, there's a couple helpful folders. Of note to start with is this config folder. So this sets up how your web application is going to, say, authenticate people or cash things or connect to the database. And what's really helpful is that Laravel allows you to set up different development environments. So what we've done here is if we go under the local folder, there's a database.php file. And you'll notice here that we set up a MySQL connection that allows individuals to connect to the MySQL server that's directly on the CS50 appliance. And we're connecting to a database that I set up called Blog50. So let's actually go ahead and run the working version of this, just to get a sense of what the application that we're building looks like. So I have a copy of this Blog50 completed. So Laravel actually has built in a server that you can run directly from the command line. So this is similar to the PSet before when you actually build your own server in C. So they have one built in so that you can run your Laravel apps directly from the command line. So if we do php artisan serve, this will launch a development server on port 8,000. So if we go to local host 8,000, you'll notice that, hey. We have our blog up and running. So Laravel here is generating the front page of our blog. Very simple application. But there's a couple really nifty features that it provides underneath the hood. So the blog application's straightforward. If we wanted to create a post, we can click on that button. We can say, "Hey, everyone. This is a really fun seminar," for example. And write something down here. Text here. If we click Submit, you'll notice that our new blog post has been added to the front page of the blog. If we go back here, you'll notice that there have been some comments already on the blog. So if we scroll down, you'll notice that Jonathan Tan says that he was very intrigued by this post. So we'll go into how object relational mapping allows you to do these relations in a fairly seamless way as well. Cool. Any questions about functionality of what we're going to build? Cool. So let's start out with actually creating the database tables. So recall that in CS50 Finance, you put together a table for the users as well as for the stocks in your portfolio. So as we mentioned earlier, what we use in Laravel is something known as migrations. So if we go back to the distribution code here, the first command that is helpful that Laravel provides you is this migrate command. So we can do php artisan migrate:make. So this allows us to create a migration. And then we'll want to create a migration called create_posts_table, which is going to be where we're going to be storing our blog posts. And you'll notice here that it runs through some code that actually generates a file with a time stamp on it. So if we go and look at Database, we'll notice under Migrations that it's created a blank file for us which has boilerplate code with the name that we specified, create posts table. And it has two functions in it. Up is what we want to run when the migration is applied to the database. And down is what we're going to do when we want to reverse a migration. So here let's start out with writing this migration. So there's a helpful class in Laravel called Schema. So we're going to run Schema::create. And we're going to create a table called posts. And here we apply this using a function. And within here, we're going to actually specify the contents of our table. We're going to create a ID, which is auto-incrementing. In addition, we're going to create a field that represents the title of our blog post. We're also going to create a field for storing the text of our blog post. And finally, we're going to store some timestamps for when our post was created and when it was updated. And for down, it's fairly simple. All we want to do is drop the table that we've created. Great. Any questions? So now if we go ahead and-- actually, local host, let me delete what we had earlier. Go to Databases. I'm going to delete what we had earlier. Drop this and create a new database Blog50. So now what the magical part is here that we can apply these migrations directly to the database using the command line tool. So if we do php artisan migrate, you'll notice that, hey. It's created the migration table, which we'll take a look at in a bit, and it's applied this first migration. So we look at Blog50, you'll notice that it's created two tables for us. First is this migrations table. So if we browse this, you'll notice that this table is fairly straightforward. It's just said that, hey. We've applied this migration. We go back and look at posts. You'll notice that the structure is exactly what we had asked it. We have an auto-incrementing ID. We have a string for storing the title, and a text field for storing the content. Great. Cool. Any questions about migrations work, how we can apply them? No? Cool. So now we're going to go ahead and actually create the model. So we want to create a posts model that stores an abstraction of the database. So rather than doing MySQL queries directly, we're going to create. So we have to create a folder in here called models. And inside here, we're going to create a file called post.php. Inside this PHP file, we're going to create a class post which extends eloquent. Eloquent is the name of the ORM engine that Laravel provides. And in here, we might expect that you actually need to write some code. We're going to write some helper functions later. But out of the box, this will already recognize what's in the database and we can access, say, the text of our blog post or the title, and create stuff directly with pretty much no code whatsoever. So that's one of the magical components. And wind once this class is more fully fledged, we'll include information about what it's related to, so the comments. Also create a function so we can actually directly get the URL of a blog post page. Cool. Any questions about that? No. Cool. So now once we have our model, we want to create a controller that's able to interface with these models, and subsequently the database. So if we take a look at BlogController, you'll notice that there's not much here right now. All there is is an index function that generates the home page, but without anything there to display yet. So the first function that we're going to create is one that allows us to create a blog post. So we're going to declare a new function called newPost. And inside here simply, we're going to set the layout of this page to be a render version, if you recall from CS50, of this template called blog.new, which we're going to create in a little bit. You'll notice here on line six that we specified this variable, layout. And if we take a look at the Views folder, there is a Layouts folder, which includes a very basic HTML file. And of note, you'll notice that we have this container here that yields content. So inside of our template what we're going to do is create what's going to be substituted directly within this layout. So we said that we want to render a template called blog.new. But inside of Blog, there's not yet this new template. So we're going to create a file called new.blade.php. This tells Laravel that this PHP file should be rendered with the blade template engine. So this is a fairly straightforward file. It's going to be the form by which we actually add in a blog post. So the magic here from the inheritance is that, hey. We want to specify that the section, the content section here, which is delineated by @section and @stop. So what's in between here is going to be substituted into the master layout. And here what we want to do is very simply create a new HTML file. Let's just add a quick title. Add a blog post. And within it, we're going to create a form. This form is going to have an action. And this is going to be something that we substitute in later, and we'll see how routing fits into here. But we're just going to define for now that this goes to a URL with the route from createPost. And then this is going to have method post. Within here, we're going to have two fields. Div class="form-group". We're using the Bootstrap CSS library graciously provided by Twitter. So we're going to create two of these. So this first input is going to be the title. So input name="title". Class="formcontrol". Type="text". I'm going to add a placeholder="Title". And then the second one is going to be a text area. Name="content". Class="formcontrol". And placeholder="Write here". There we go. Finally, we're going to add a quick submit button. Type="submit" class="btn btn-primary". So these are all features of Bootstrap so that this can be laid out in a way that's prey to the user, rather than naked HTML. So we've defined the controller here. We've defined a very simple view. But what's missing is the connective tissue. So at this point, Laravel has no idea how we're going to actually access this controller. So this is defined in a file called routes.php. And right now, we have one route. Which is when we go to the home path for this website, it's going to render the index controller. So here what we need do is implement a new route for us to create a post. So we use this method get, which specifies that when a user tries to get this page-- specifically the post slash new page-- what we're going to do is use the controller called BlogController new post. The one that we just created. And then we're going to alias it as newPost. We're going to create another function in a little bit. But what's here under the key "as" is what we can substitute within our Blade templates. So for now, let's actually also say route. So we're also going to create a controller so that we can create these posts. So if a user posts to the page post slash new, what we're going to do is uses a controller that we're going to create shortly called BlogController at createPost. And we're going to alias this with as createPost. Cool. Any questions? Cool. So let's run what we have so far. So if we do php artisan serve, we will see lots of errors. So it looks like we have a syntax error on routes line 27. Ah. Missing a semicolon. So if we go to 8,000, you'll see nothing here yet. So this is the default home page. But if we go to post slash new, hey. It'll be the form that we just created. Right now, we haven't implemented the functionality of when we press the Submit button. So if we click on the Submit button, it's going to run an error. But we're going to code that right now, precisely what we want to do when a user submits this form. So let's do that. Go back to the Controllers file. What we're going to do is implement this new function that allows us to create a post. Declare new function. Public function createPost. And this function is going to be a little bit more sophisticated than what we had before. But you'll see here that we're not going to actually write any SQL. The ORM, the Eloquent ORM, is going to allow us to do this in some ways a more elegant way. So we're going to create a new post. And here we're instantiating a new object from the model that we just created, the post model. And what we're going to do is set the title attribute of this using something that we get from the server. So this is similar to what we had before in CS50 Finance where we would do, using the Super Global Post looking for title. So Laravel provides some sanitation and whatnot using this helper function. So we would prefer to use this instead of this very basic raw form from PHP. And then what we're going to do is set the content of it to Input get content. We're actually going to wrap this in a helpful function that PHP provides called nl2br, which turns new lines, nls, into brs, breaks, so that we can actually have different paragraphs within it. And finally what we're going to do is save this post. So we call the function save on this model. We're going to save the post. And finally what we're going to do is redirect the user specifically to the route which we're going to create shortly, alias by viewPost. And we're going to pass in the arguments id, being the id of this new post. Great. So now if we actually go and run this. We're going to add a new post. Let's say this is seminar 50. And say, sure. Asdl. Whatever. Some sort of content. And Submit it. And we'll notice that, hey. routes not defined. But if we take a look at phpMyAdmin and look for whether or not our function did anything. Look at Blog50 posts. We'll notice that, hey. In fact, we did just create that blog post with the timestamps as specified. So now let's go back and actually create this other function in our controller, specifically the viewPost controller. So public function viewPost. So here what we'll do, instead of having empty parentheses, we'll want to pass in the ID of the post that we're creating. And from here what we're going to do is actually query the database for it. So if we do post, there's a function called Find that allows us to query it by ID. Specifically, actually, we're going to use an alternate version of this called Find or Fail, which allows us to quit out of this function, throw an exception if the ID we pass in does not exist. And then we're going to do something similar to what we did earlier where we set the content of this page to be the rendered version of this new view which we're going to create, blog.view. And we're going to pass into it-- just like in the CS50 render function-- a dictionary of variables. The keys of this associative array become variables within the template. So we're going to do post post. So pass indirectly the post that we've queried from the database. . Now what we're going to do is create this view so that we can actually view the blog posts that we've built. So we're going to create a file called view.blade.php. So inside of this template, what we're going to do is put together a simple page that allows us to display the content. So we do section, that's before content. Stop. And what we're going to do within here is write some HTML to display this page. So we're going to wrap it with a fancy new HTML5 element called Article. And in here, we're going to have a header where we're going to simply have an h1, which includes the post title. So here, if we look at this double curly brace notation, this will do essentially PHP echo post title. So it's a helpful shorthand that Laravel provides us. So we're going to use this notation instead. And here, we're going to also print out the content of it. And in here, we're going to do post content. And down at the bottom what we're going to do is create a footer. And in the footer, we're going to first display when this was posted. So this was posted at, created at. And Laravel uses a really nice date library called Carbon. So we can actually do something called difforHumans, which you saw earlier. When we posted. it'll say, like, five seconds ago. So this is a really nice functionality of Laravel. And finally, we're going to close this footer. So now if we go back to Home Page we're going to see nothing here yet because we haven't coded up the home page. But if we go to post slash one, we're going to see an exception. Does anyone know why we see an exception? What are we missing? Any ideas? So what did we do earlier for us actually to define how we get to particular controllers? SPEAKER 1: The route? ERIC OUYANG: Yeah. So we still have to define the route. So we go back here to routes.php. You'll notice that we haven't actually defined how we're going to get to this controller. So now we're going to define this route. It's fairly straightforward, similar to what we did earlier. But what we're going to notice here is that we're going to have a placeholder. So if we do route.get slash post ID. So ID now is what's going to be passed into the controller. This is going to use the controller that we just created, BlogController at viewPost. And we're going to alias this as viewPost. Great. So now we're going to create this route. So now if we go here and refresh this page, we in fact do have our new blog post. So this is what we created earlier. Very simple page, but displays the blog post that we just created. Cool. And if we actually go through the whole process of creating a new blog post, we'll notice that everything redirects properly. If I say, "Hi. I'm Jonathan Tan." Say, "This is my blog post." And Submit it, this will create this new blog post with ID 2, which increments from what we had previously and displays it properly. Awesome. Any questions? Yes? SPEAKER 2: Does Laravel handle sanitization and everything for you? ERIC OUYANG: Yes. So when we saw earlier when we did input colon colon get, that sanitates any SQL injections and whatnot that we might want to perform if we're a malicious user of the website. So Laravel handles a lot of that behind the scenes. Good question. So let's take a look at the home page. So if we first go back to the controller for the home page, you'll notice that it doesn't do much here. You'll notice that we're not passing into this controller anything particularly helpful. It's just this index file. So let's pass into this something helpful. And specifically, we're going to pass in posts. And Laravel allows us to do post all, which will allow us to get all the posts. Now if we go back to index.php, you'll see, hey. Nothing here yet. But what we want to do here is actually iterate through, do a foreach loop over the posts that allows us to print out the posts. So foreach posts as post, what we want to do is print out the content of the blog post. But one thing you'll notice is that we actually wrote most of that code already, in view.blade.php. So what we're going to actually do is use a nice helpful feature of Blade and factor out this common code. So we go here. We're going to take this content right here, and what we're going to do is create a new folder. Let's just call it Partials. And in here, we're going to create a post.blade.php. So here, we factored out the way in which we want to display these posts. And here what we'll do instead of actually having that HTML directly, we're going to use this directive called include blog.partials.post. And what we're going to do here is pass in the post of the page. So now if we go back here, we'll notice that the functionality is still the same. But now we have this factored out code, this HTML. So we can use it in Index. So here, this is very straightforward. All we do is include blog.partials.post and array. And somewhere to before, we do post post. So now if we go back to the home page, we'll see that, hey. We have a list of all the blog posts that we had before. We might want to add some "if" conditions and "else" conditions so that if we don't have anything on the blog, we want to display something helpful. Like, hey. There's no content yet on the blog. And if you guys actually look at the distribution code on GitHub, you'll see an example of how we do that. Cool. Any questions? Yes. SPEAKER 2: I guess just a fundamental question. Back at the route. ERIC OUYANG: Yeah. If we take a look at routes. SPEAKER 2: Where does the uses.blogcontroller at create post, what does that direct us to? ERIC OUYANG: Yeah. Yeah. SPEAKER 2: Or what is that-- ERIC OUYANG: So, take a look, say for example, this route right here. The first part is the actual URL that the user will go to. And this array here, associative array, defines how we want to have the application act in response to it. So uses is the controller, the function that we want to call when a user goes to this URL. So viewPost right here was a function that we defined inside of BlogController-- SPEAKER 2: I see. ERIC OUYANG: --so that we can actually render a view, perform some calculations, interact with the SQL database. SPEAKER 2: OK. ERIC OUYANG: And then the other part, "as" is an alias that we use. So if we notice when we created the form, you'll notice that URL::routecreatePost. So it substitutes in the actual URL so that we're not hard coding these in so we can change it once, say if we wanted to rename. Instead of post slash new, we want to do like, p slash new just to clean up our URLs a bit. We would change it in one spot rather than across all the different files. Cool. That's good. So now we have a very basic blog platform. We probably want to add in a button so that we can actually create new posts. So if we take a look at the Layout Master, we have a section called Header Right that we've just defined up at the top. So we can add buttons to the top of the header. So if we go to index.blade.php, actually do is define what goes inside of here. So this Header Right section, what we're going to do is add in a button to go to the URL defined by the route new post. Just to clean it up and make it pretty and all Bootstrap, we're going to make this a default button. Let's just make it large for fun. And inside it, we could put some text. But what Bootstrap provides is glyphicons. So we can actually add in that nice pencil that we saw earlier. Glyphicon. Glyphicon-pencil. So this will allow us to put in an icon instead of text. Now if we do stop, this will define this section. And, hey. We have a nice button that links us directly to the add a blog post page. So we have a fairly simple blog. We can add stuff to it. But what we generally expect from blogs is commenting. So it's really important for us to have, say, if someone else visits a website and really likes the post that they can engage in discussion with other people who visit the page. So we're going to go and create a new database table and a new model so that we can associate comments with posts. So the first step, just like before, is that we need to run a migration. So like before, we do php artisan migrate:make. And we're going to create one called create_comments_table. This will create a file that has our new migration. And we're going to, like before, define a new table. So Schema::create a table called comments. This function here. And inside this table what we're going to do is first, like before, assign an ID. Increments ID. We're going to allow users to associate their name with a particular comment. We're going to have some content that goes along with this, text content. And here what we're going to do is something different. We're going to create an integer that's called post_id which is going to signify what post a particular comment goes with. In addition, we're actually going to set a foreign key constraint on this. So MySQL will enforce that. We're not trying to assign comment number 5 to post 5,000 if we haven't had 5,000 posts on it. So what we do here, we do foreign post_id is going to be associated with the ID field from the table posts. And we'll actually do something else helpful, is onDelete. So if we delete some post from the database, that we want to cascade the deletes for comments as well. Because it's not very helpful for us to have comments on posts that don't exist. And finally, like before, we're going to set time stamps on this. And like before, we're going to have the reverse migration be dropping the comments table. So now if we go back to here, we're going to run this migration, artisan migrate. And now it's applied this migration that we just created. So if we a look at phpMyAdmin, we do, in fact, now have a comments table that has the structure that we just specified. So like before, we're going to create a new model to abstract the SQL table that we just created. So let's Add a new file. We're going to call it comment.php. And this is actually going to be fairly straightforward with a slight change from what we had before. So class Comment extends Eloquent. And what we're going to do here is define a function that is the relationship with other models. So we're going to have a post function here that returns this relationship. So we're specifying that this belongs to post, saying that there is one post that this comment belongs to. This actually has to be capital P for the model. And now just on the other flip side, we have to say that, hey. Posts have comments. So what we're going to do is define public function comments. And here is returned this has many Comment. So now magically when we have a post, we can get the attribute comments and it'll populate it with the information from the database. So let's actually go through and add a new feature to our view file so that we can both display and create comments. So we're going to define a new section. Let's just separate it with a horizontal rule. Section id="comments". What we're going to do here is, like before, iterate through all the comments. So actually how we do this is, as I mentioned, fairly magical. We do post comments. And then we can do this for each loop over each of the comments. And what we're going to do is div class comment, and we're going to actually print out this comment. So display that hey, comment name-- the person who posted this comment-- says dot dot dot. We're going to put this in a block quote, just to make it look nice. And then comment content block quote. And foreach. So now this is going to loop through all the comments that is associated with each of the posts and display each of those comments. I'm going to add another section down here, which allows us to add a comment. So h3 class. Put a title here. Add a comment. And we're going to define a new form. So like before, we're going to do form action. And here, the new action is we're going to define a new controller that allows us to respond to post requests for creating comments. So URL::route createComment. I'm going to pass in the parameter here. The ID of the post that we're creating comment on. And then the method of this form is going to be post. Now we're going to add in a two fields, form group. This is going to be an input with the name "name" and class="form-control", type="text", and with the placeholder="Your name." We're also going to define another form field, which is going to be a text area as we had before. Just like before, call it content. Class="form-control." Placeholder="Write here." And just so that we can actually submit it, submit type and class="btn btn-primary." Close the form. Close this action. So now if we refresh this page where we have, say, a particular post. We have to reboot the server. PHP artisan serve. Reboot this. We have to define the route. But for now, let's just take this out so that we can actually show you what the page looks like, and then we'll actually create that route. So, hey. We have this new form down here so that we can create comments. So let's actually define a function within the controller so that we can add comments. Let's go back. And within blogcontroller.php, what we're going to do is create a new function called create comment. Public function createComment. This is going to have a single parameter, the ID of the posts that we're commenting on. And like before, we're going to first get the post. So post, findOrfail id. Afterwards, we're going to create a new comment. So comment = new Comment. Comment name = Input::get name. Comment content = the same new line into breaks of Input::get content. And finally, we're going to have to associate this comment with the post. So we're going to use this function, comments, that allows us to save this relationship. So now this comment will have automatically the post ID. We could also set it manually, but this is more easy to read as far as the function goes. And after we perform this, what we want to do is redirect the user to the route specified by viewPost with the array with the parameter of the post ID. And now so that this actually functions, we need to define this route. Route::post. And now we're going to call this post slash ID slash comment. Array uses the new function that we just created. BlogController. CreateComment as createComment. Great. So now hopefully if we refresh this page and add in a comment, say, David Malan. "Hopefully this works." Submit. We do, in fact, have a comment on this blog post. Cool. So now we have a fairly functional blog post. We're just going to add a couple tweaks so that we have some more useful information on these posts. So if we go back to the front page, we have no sense of how many comments are on each of these posts. So what we're actually going to do is, inside our model, define a helper function that allows us to specify the number of comments that go with a particular post. So we're going to create a helper function. Public function. GetNumCommentsStr. So a string that specifies the number of comments that go along with it. And what we're going to do is say that num = this comments count. So we're going to count the number of comments. And if this number equals 1, we're just going to return 1 comment. And then otherwise, we want to return the concatenation of num and comments, so we get the pluralization correct. Just make this a single quote. One comment. And now we can use this function directly inside of our view. So if we go back to the partial posts that we created, now we want to actually display the number of comments. So what we can do is post, use this function that we just created to display the number of comments. So if we now refresh, it does, in fact, display the number comments that go along with it. If we wanted to be fancy if you actually look at the distribution code, we can actually link this to the comments. If you recall, we defined within the view that this has section ID comments. So if we actually wanted to link directly to the comments section, what we would do here is a href URL of the route viewPost. Pass in the array ID of post ID. And then we want to go to specifically the comments section. Here let's close the A tag. So now if we refresh this page, we click on this. We'll go directly to the comments section. If we had a longer post, you can actually see this bounce down. But you'll notice that it's not at the top of the page. Cool. Great. So that's a fairly straightforward example of something simple that you can do with Laravel. But you can notice here that we've done a lot of things with fairly little amount of code. Laravel allows us to do the SQL queries behind the scenes. It does the sanitation for us behind the scenes. Allows us to do these relationships very easily without us needing to do any SQL join statements to combine comments with what the posts. Allows us to do this inheritance of templates so that we can define these nesting files so we're not repeating ourselves, just like when we had that display of the blog posts that we don't have to copy and paste the code. And from here you can build increasingly complicated applications. You can imagine if we wanted to implement log-in, we could say, bring in a third party framework that allows us to do that. There's a bunch of them that are really, really great that can do like, password recovery. And it'll send you a reset password email. We can implement permission so that I can create a post, but someone else can't edit it. We can implement functionality to delete posts. But you can see here that we have pretty much all the rudimentary components to build some really, really, dynamic and exciting web apps. So with that, I think we're good. Do you guys have any questions? Yes? SPEAKER 3: How do you get static content? ERIC OUYANG: Static content. So you saw before that when we had this right here, this layout content, view make, we had this without this array afterwards. Blog.index, we had this as just a static file. So if we don't pass anything along to it, it'll just render the HTML directly. But if we pass in this associative array of posts, which is dynamically pulled from the database, we can make the page dynamic. Cool. Any other questions? SPEAKER 3: How would you compare Laravel to maybe some other options? ERIC OUYANG: Sure. Yeah. So Laravel is-- that's a great question-- one of many options for web frameworks. So Ruby on Rails is one that's popular. I believe Twitter used to be implemented with Ruby on Rails. I think they've since switched. There's another one called FuelPHP. So Ruby on Rails uses the Ruby language and implements a lot of the MVC stuff that we see here. FuelPHP is another PHP framework. Django is one of my favorites. It's a web framework for Python. So you can write your web app in Python. So there's a ton of these options. Laravel, I think, by and large is my favorite right now for PHP just because of the components that we talked about earlier. It's Composer enabled. It includes a really, really expressive ORM system. Also has a really awesome templating language that some of the others just don't provide. And migrations. Migrations are awesome as well. Cool? Awesome. Well, thanks so much for watching this seminar, and good luck on your final projects.