{"captions":[{"content":"[Seminar] [Kohana: A Lightweight PHP Framework]","startTime":0,"duration":2150,"startOfParagraph":false},{"content":"[Brandon Liu] [Harvard University]","startTime":2150,"duration":1850,"startOfParagraph":false},{"content":"[This is CS50.] [CS50.TV]","startTime":4000,"duration":3270,"startOfParagraph":false},{"content":"Hi everyone. My name is Brandon.","startTime":7270,"duration":1860,"startOfParagraph":true},{"content":"I'm a junior here at the college doing computer science, and today we're going to talk about","startTime":9130,"duration":1870,"startOfParagraph":false},{"content":"Kohana, which is a PHP web development framework.","startTime":11000,"duration":3460,"startOfParagraph":false},{"content":"Today is going to be a live coding seminar,","startTime":14460,"duration":2800,"startOfParagraph":false},{"content":"so I'm basically going to spend 5-10 minutes explaining what Kohana is,","startTime":17260,"duration":3740,"startOfParagraph":false},{"content":"and then I'm literally going to build a super simple blog","startTime":21000,"duration":4000,"startOfParagraph":false},{"content":"for you right here literally from scratch.","startTime":25000,"duration":3570,"startOfParagraph":false},{"content":"We're going to download the code from the Kohana website,","startTime":28570,"duration":3659,"startOfParagraph":false},{"content":"and we're going to start building a blog, and hopefully it'll be very instructive,","startTime":32229,"duration":2771,"startOfParagraph":false},{"content":"because you'll see perhaps maybe I'll make some mistakes,","startTime":35000,"duration":2000,"startOfParagraph":false},{"content":"and you'll see me recover from them,","startTime":37000,"duration":2000,"startOfParagraph":false},{"content":"or you'll see my thought process as I build through this blog,","startTime":39000,"duration":3040,"startOfParagraph":false},{"content":"and meanwhile, you'll also get familiar with the framework itself.","startTime":42040,"duration":1960,"startOfParagraph":false},{"content":"Hopefully it'll be a very instructive exercise.","startTime":44000,"duration":4000,"startOfParagraph":false},{"content":"First, what exactly is a framework?","startTime":48000,"duration":3370,"startOfParagraph":true},{"content":"If you've been taking CS50 so far, you haven't really worked with any ","startTime":51370,"duration":2630,"startOfParagraph":false},{"content":"frameworks yet, and the thing is this.","startTime":54000,"duration":2000,"startOfParagraph":false},{"content":"You've probably done 1 web development pset already,","startTime":56000,"duration":4000,"startOfParagraph":false},{"content":"and let's say you continue to build websites and keep building websites.","startTime":60000,"duration":3000,"startOfParagraph":false},{"content":"You'll start to notice a few things.","startTime":63000,"duration":2000,"startOfParagraph":false},{"content":"The first thing you'll notice is that you're probably doing the same things","startTime":65000,"duration":2000,"startOfParagraph":false},{"content":"over and over again all the time,","startTime":67000,"duration":2150,"startOfParagraph":false},{"content":"things like cleaning user input data, ","startTime":69150,"duration":3850,"startOfParagraph":false},{"content":"things like organizing your files in a certain way.","startTime":73000,"duration":4250,"startOfParagraph":false},{"content":"The other thing you'll also probably notice is that your code","startTime":77250,"duration":1750,"startOfParagraph":false},{"content":"may start to become very, very messy,","startTime":79000,"duration":2000,"startOfParagraph":false},{"content":"and you may leave it messy and just have a very hard time maintaining it,","startTime":81000,"duration":3000,"startOfParagraph":false},{"content":"or you may start to structure your code and making it modular","startTime":84000,"duration":2000,"startOfParagraph":false},{"content":"in certain ways to make it more maintainable.","startTime":86000,"duration":2510,"startOfParagraph":false},{"content":"This is where web frameworks basically came in.","startTime":88510,"duration":2510,"startOfParagraph":true},{"content":"These people who had built a lot of websites, they said,","startTime":91020,"duration":2660,"startOfParagraph":false},{"content":"\"We don't need to redo this every single time we build a website.\"","startTime":93680,"duration":2140,"startOfParagraph":false},{"content":"\"Why don't we just make a package that does all these things for you ","startTime":95820,"duration":3230,"startOfParagraph":false},{"content":"every single time you want to build a website?\"","startTime":99050,"duration":2200,"startOfParagraph":false},{"content":"And so that when you make a new website,","startTime":101250,"duration":1780,"startOfParagraph":false},{"content":"you just focus on what exactly this particular website is about.","startTime":103030,"duration":2850,"startOfParagraph":false},{"content":"You don't need to repeat all the various configuration","startTime":105880,"duration":2750,"startOfParagraph":false},{"content":"and separation of codes and rewriting code","startTime":108630,"duration":4440,"startOfParagraph":false},{"content":"that you often have to do when you're making websites.","startTime":113070,"duration":3110,"startOfParagraph":false},{"content":"The idea is that a framework allows you to write a higher-level code","startTime":116180,"duration":3230,"startOfParagraph":false},{"content":"without having to worry about lower-level details.","startTime":119410,"duration":1620,"startOfParagraph":false},{"content":"A lower-level detail might be something like","startTime":121030,"duration":2640,"startOfParagraph":false},{"content":"dealing with cleaning user input data.","startTime":123670,"duration":3580,"startOfParagraph":false},{"content":"That's something that you shouldn't really need to worry about.","startTime":127250,"duration":2070,"startOfParagraph":false},{"content":"You should focus on what your web allocation is actually about.","startTime":129320,"duration":3050,"startOfParagraph":false},{"content":"It eliminates a lot of boilerplate code.","startTime":132370,"duration":2420,"startOfParagraph":false},{"content":"It is an architecture for your project.","startTime":134790,"duration":1850,"startOfParagraph":false},{"content":"The most popular one would be Model-View-Controller,","startTime":136640,"duration":2040,"startOfParagraph":true},{"content":"which I'm going to talk about in a second.","startTime":138680,"duration":1700,"startOfParagraph":false},{"content":"And a lot of times these frameworks embody a set of procedures,","startTime":140380,"duration":2410,"startOfParagraph":false},{"content":"rules, and best practices for you to use ","startTime":142790,"duration":2960,"startOfParagraph":false},{"content":"so that when you adopt the web framework","startTime":145750,"duration":2440,"startOfParagraph":false},{"content":"you have to write your code in a certain way,","startTime":148190,"duration":1860,"startOfParagraph":false},{"content":"and it's generally an agreed upon set of principles","startTime":150050,"duration":2380,"startOfParagraph":false},{"content":"by the community that is generally accepted to be","startTime":152430,"duration":1860,"startOfParagraph":false},{"content":"a good way of writing code.","startTime":154290,"duration":1940,"startOfParagraph":false},{"content":"It makes your code more maintainable, more usable,","startTime":156230,"duration":2030,"startOfParagraph":false},{"content":"so on and so forth.","startTime":158260,"duration":1770,"startOfParagraph":false},{"content":"And finally, the thing I want to emphasize about frameworks","startTime":160030,"duration":2710,"startOfParagraph":false},{"content":"versus libraries is this idea about inversion of control, and the thing is this.","startTime":162740,"duration":3240,"startOfParagraph":false},{"content":"The difference between a library and a framework is that with a library","startTime":165980,"duration":2450,"startOfParagraph":false},{"content":"you are still writing the main program,","startTime":168430,"duration":3560,"startOfParagraph":false},{"content":"and you're sort of invoking the library","startTime":171990,"duration":2470,"startOfParagraph":false},{"content":"and calling upon the library to do something for you.","startTime":174460,"duration":2350,"startOfParagraph":false},{"content":"The difference between a library and a framework","startTime":176810,"duration":1370,"startOfParagraph":false},{"content":"is that the framework starts out with the control,","startTime":178180,"duration":3440,"startOfParagraph":false},{"content":"and it invokes your code,","startTime":181620,"duration":1640,"startOfParagraph":false},{"content":"so you can think of it as--this is why it's called a framework--","startTime":183260,"duration":2930,"startOfParagraph":false},{"content":"the framework provides this frame and structure for your code,","startTime":186190,"duration":3510,"startOfParagraph":false},{"content":"and you fill in the holes,","startTime":189700,"duration":1620,"startOfParagraph":false},{"content":"and this will become more apparent in a second","startTime":191320,"duration":2670,"startOfParagraph":false},{"content":"when you see me start to write code within the context of the framework.","startTime":193990,"duration":2680,"startOfParagraph":false},{"content":"You'll see that I'm filling in the gaps,","startTime":196670,"duration":2460,"startOfParagraph":false},{"content":"and the framework is kind of controlling all the moving pieces,","startTime":199130,"duration":3320,"startOfParagraph":false},{"content":"and I have to put the pieces in the right places.","startTime":202450,"duration":4970,"startOfParagraph":false},{"content":"Today we're going to talk about Kohana, ","startTime":207420,"duration":1940,"startOfParagraph":true},{"content":"which is one of many PHP frameworks.","startTime":209360,"duration":2250,"startOfParagraph":false},{"content":"There are web frameworks, and there are ones in virtually every single language,","startTime":211610,"duration":2940,"startOfParagraph":false},{"content":"and I'm picking Kohana because Kohana is arguably","startTime":214550,"duration":3780,"startOfParagraph":false},{"content":"and generally recognized as the easiest PHP framework to pick up.","startTime":218330,"duration":4920,"startOfParagraph":false},{"content":"It's the most lightweight.","startTime":223250,"duration":2690,"startOfParagraph":false},{"content":"There are other ones out there that come with many, many more features,","startTime":225940,"duration":2530,"startOfParagraph":false},{"content":"but they tend to be more difficult to pick up.","startTime":228470,"duration":3120,"startOfParagraph":false},{"content":"And finally, Kohana uses the MVC architecture.","startTime":231590,"duration":3330,"startOfParagraph":false},{"content":"It's lightweight enough that we can literally build a project","startTime":234920,"duration":2860,"startOfParagraph":false},{"content":"right here right in front of your eyes, and you can pretty much","startTime":237780,"duration":1840,"startOfParagraph":false},{"content":"follow along pretty easily.","startTime":239620,"duration":3160,"startOfParagraph":false},{"content":"What is the MVC architecture?","startTime":242780,"duration":1640,"startOfParagraph":true},{"content":"It stands for Model-View-Controller,","startTime":244420,"duration":2120,"startOfParagraph":false},{"content":"and maybe if you think about the code you've been writing so far","startTime":246540,"duration":3020,"startOfParagraph":false},{"content":"for some of your web development psets ","startTime":249560,"duration":1720,"startOfParagraph":false},{"content":"you may be able to see some of this, but usually when you start writing","startTime":251280,"duration":3430,"startOfParagraph":false},{"content":"a more complex web application,","startTime":254710,"duration":1790,"startOfParagraph":false},{"content":"the division between these 3 segments becomes more and more evident.","startTime":256500,"duration":4709,"startOfParagraph":false},{"content":"I laid out the MVC here sort of as a stack,","startTime":261209,"duration":5531,"startOfParagraph":false},{"content":"and often you'll hear people talk about stacks in web development,","startTime":266740,"duration":3180,"startOfParagraph":false},{"content":"and this is to illustrate the idea that","startTime":269920,"duration":3840,"startOfParagraph":false},{"content":"each layer, each component really tries to only communicate","startTime":273760,"duration":3750,"startOfParagraph":false},{"content":"between 2 other components.","startTime":277510,"duration":3240,"startOfParagraph":false},{"content":"Someone accesses your website as a client or a browser.","startTime":280750,"duration":3800,"startOfParagraph":false},{"content":"They interact with your program through the view code.","startTime":284550,"duration":4320,"startOfParagraph":false},{"content":"The view code interacts with the controller.","startTime":288870,"duration":1630,"startOfParagraph":false},{"content":"The controller interacts with the model, ","startTime":290500,"duration":1900,"startOfParagraph":false},{"content":"and the model interacts with the SQL database.","startTime":292400,"duration":2690,"startOfParagraph":false},{"content":"And there is no hopping in between if you write your code properly.","startTime":295090,"duration":4580,"startOfParagraph":false},{"content":"What do these things do?","startTime":299670,"duration":2000,"startOfParagraph":true},{"content":"The model essentially is the piece of code that deals with your data.","startTime":301670,"duration":4350,"startOfParagraph":false},{"content":"Anything that deals with your database, with the objects that you store,","startTime":306020,"duration":3080,"startOfParagraph":false},{"content":"or retrieving those objects in the database,","startTime":309100,"duration":2180,"startOfParagraph":false},{"content":"that's all handled by the model.","startTime":311280,"duration":2170,"startOfParagraph":false},{"content":"Maybe you have objects in your database.","startTime":313450,"duration":1840,"startOfParagraph":false},{"content":"We're going to create a model having to do with posts,","startTime":315290,"duration":2410,"startOfParagraph":false},{"content":"so a post may have some attributes to it.","startTime":317700,"duration":2060,"startOfParagraph":false},{"content":"You may have functions around storing those posts or retrieving posts","startTime":319760,"duration":4140,"startOfParagraph":false},{"content":"or filtering the posts and so on and so forth,","startTime":323900,"duration":2370,"startOfParagraph":false},{"content":"and that's all the code that's handled by the model.","startTime":326270,"duration":2610,"startOfParagraph":false},{"content":"The controller is sort of the application logic,","startTime":328880,"duration":2900,"startOfParagraph":false},{"content":"and a lot of different things can go in the application logic.","startTime":331780,"duration":6130,"startOfParagraph":false},{"content":"If you're talking to a different API,","startTime":337910,"duration":1880,"startOfParagraph":false},{"content":"that may be where you're dealing with the application logic.","startTime":339790,"duration":2520,"startOfParagraph":false},{"content":"If you're trying to have to bring in data from multiple different models","startTime":342310,"duration":5680,"startOfParagraph":false},{"content":"and have to combine them in some way, that often may be handled by the controller.","startTime":347990,"duration":3550,"startOfParagraph":false},{"content":"For example, on Facebook, if you friend someone,","startTime":351540,"duration":2280,"startOfParagraph":false},{"content":"then perhaps that act of establishing that relationship","startTime":353820,"duration":4680,"startOfParagraph":false},{"content":"may be done by the controller.","startTime":358500,"duration":1990,"startOfParagraph":false},{"content":"And finally, the view is the code that's generating what you actually see.","startTime":360490,"duration":3860,"startOfParagraph":true},{"content":"A lot of times I think in the CS50 psets","startTime":364350,"duration":3060,"startOfParagraph":false},{"content":"they don't really encourage you guys to separate these 3 things.","startTime":367410,"duration":2640,"startOfParagraph":false},{"content":"You'll probably have this big, long file where at the top","startTime":370050,"duration":2380,"startOfParagraph":false},{"content":"you make some SQL query and maybe do some processing","startTime":372430,"duration":2700,"startOfParagraph":false},{"content":"on the data you retrieved from the database,","startTime":375130,"duration":1890,"startOfParagraph":false},{"content":"and then you have all your HTML at the bottom.","startTime":377020,"duration":2350,"startOfParagraph":false},{"content":"And you may find that as you create more and more pages","startTime":379370,"duration":3470,"startOfParagraph":false},{"content":"that you're going to have some code repetition, and also,","startTime":382840,"duration":2950,"startOfParagraph":false},{"content":"the thing is your file gets really big and long","startTime":385790,"duration":3810,"startOfParagraph":false},{"content":"and becomes unwieldy to manage.","startTime":389600,"duration":2770,"startOfParagraph":false},{"content":"The reason why MVC is so well regarded","startTime":392370,"duration":2130,"startOfParagraph":false},{"content":"is for a number of reasons.","startTime":394500,"duration":1620,"startOfParagraph":false},{"content":"The first thing is something called separation of concerns","startTime":396120,"duration":1990,"startOfParagraph":false},{"content":"which is the idea that when you have--","startTime":398110,"duration":2260,"startOfParagraph":false},{"content":"ideally 1 piece of code should do 1 thing and do it really well,","startTime":400370,"duration":3150,"startOfParagraph":false},{"content":"and you shouldn't combine pieces of code that do disparate things.","startTime":403520,"duration":3690,"startOfParagraph":false},{"content":"For example, view code and model code,","startTime":407210,"duration":2020,"startOfParagraph":false},{"content":"they don't really have to be related.","startTime":409230,"duration":1810,"startOfParagraph":false},{"content":"They don't have to be in the same files, so when you can, separate them out","startTime":411040,"duration":2250,"startOfParagraph":false},{"content":"so it's easy to maintain.","startTime":413290,"duration":2010,"startOfParagraph":false},{"content":"The other thing is code reuse.","startTime":415300,"duration":1830,"startOfParagraph":true},{"content":"You may find yourself writing the same SQL query or doing ","startTime":417130,"duration":2640,"startOfParagraph":false},{"content":"similar queries that could be abstracted into 1 function,","startTime":419770,"duration":3290,"startOfParagraph":false},{"content":"and that's the idea behind models and controllers,","startTime":423060,"duration":2380,"startOfParagraph":false},{"content":"having it in a separate function that you can reuse in different places in your project.","startTime":425440,"duration":4250,"startOfParagraph":false},{"content":"And finally, that's tied to DRYing your code,","startTime":429690,"duration":2730,"startOfParagraph":false},{"content":"or not repeating yourself, don't repeat yourself.","startTime":432420,"duration":2280,"startOfParagraph":false},{"content":"This is very comprehensible in development.","startTime":434700,"duration":3390,"startOfParagraph":false},{"content":"Whenever you can, you don't want to repeat yourself, because if you repeat yourself,","startTime":438090,"duration":2020,"startOfParagraph":false},{"content":" it's much more costly to maintain.","startTime":440760,"duration":2640,"startOfParagraph":false},{"content":"If you want to change 1 thing, you have to change it everywhere,","startTime":443400,"duration":2640,"startOfParagraph":false},{"content":"and that leads to bugs, and it's horrible.","startTime":446040,"duration":4190,"startOfParagraph":false},{"content":"All right.","startTime":450230,"duration":1780,"startOfParagraph":true},{"content":"Any questions so far about Kohana at all?","startTime":452010,"duration":5260,"startOfParagraph":false},{"content":"Great.","startTime":457270,"duration":1930,"startOfParagraph":false},{"content":"Now we're going to dive into the live coding session,","startTime":459200,"duration":3100,"startOfParagraph":false},{"content":"and hopefully everything goes well.","startTime":462300,"duration":5750,"startOfParagraph":false},{"content":"I am going to basically build this website","startTime":477200,"duration":2850,"startOfParagraph":false},{"content":"on one of my remote servers, and that way you guys can also","startTime":480050,"duration":3860,"startOfParagraph":false},{"content":"see the website and access the website,","startTime":483910,"duration":2400,"startOfParagraph":false},{"content":"and also the environment is better configured than my remote machine,","startTime":486310,"duration":4680,"startOfParagraph":false},{"content":"because it's running Linux instead of OS X.","startTime":490990,"duration":3590,"startOfParagraph":false},{"content":"We're literally going to start.","startTime":494580,"duration":1680,"startOfParagraph":false},{"content":"KohanaFramework.org.","startTime":496260,"duration":1850,"startOfParagraph":false},{"content":"I'm going to download the code from the website.","startTime":498110,"duration":3240,"startOfParagraph":false},{"content":"I'm going to copy the link address,","startTime":501350,"duration":3260,"startOfParagraph":false},{"content":"go to my server, download it,","startTime":504610,"duration":5160,"startOfParagraph":false},{"content":"and I'm going to extract it.","startTime":509770,"duration":3230,"startOfParagraph":false},{"content":"[Student] What's the largest you can make the text?","startTime":518330,"duration":7380,"startOfParagraph":true},{"content":"[Brandon Liu] Is that better?","startTime":525710,"duration":1620,"startOfParagraph":false},{"content":"[Student] Is that doable?>>[Brandon Liu] Yeah, that's fine.","startTime":527330,"duration":2740,"startOfParagraph":false},{"content":"I downloaded a ZIP file and unzipped that into a directory called Kohana,","startTime":530070,"duration":4430,"startOfParagraph":false},{"content":"and we're going to rename that CS50-Kohana,","startTime":534500,"duration":4320,"startOfParagraph":false},{"content":"and let's go in.","startTime":538820,"duration":2320,"startOfParagraph":false},{"content":"Awesome.","startTime":541140,"duration":2470,"startOfParagraph":false},{"content":"Here you see a bunch of different files.","startTime":543610,"duration":3260,"startOfParagraph":false},{"content":"Most of you can ignore--we're not going to go through every single file that's in here","startTime":546870,"duration":3270,"startOfParagraph":false},{"content":"because of our time constraints,","startTime":550140,"duration":2990,"startOfParagraph":false},{"content":"but generally when you install Kohana, the first thing you do","startTime":553130,"duration":3180,"startOfParagraph":false},{"content":"is you go to the directory,","startTime":556310,"duration":6900,"startOfParagraph":false},{"content":"and you'll basically do some environment tests and whatnot","startTime":563210,"duration":2840,"startOfParagraph":false},{"content":"to make sure your environment is properly set to run Kohana","startTime":566050,"duration":2590,"startOfParagraph":false},{"content":"and make sure that everything is all right.","startTime":568640,"duration":2810,"startOfParagraph":false},{"content":"You can see most things passed, but generally you always run into this 1 problem ","startTime":571450,"duration":4060,"startOfParagraph":false},{"content":"where it complains that some directory is not writable,","startTime":575510,"duration":2670,"startOfParagraph":false},{"content":"and that's because of some permissions.","startTime":578180,"duration":2230,"startOfParagraph":false},{"content":"I don't know how much you guys have learned about file permissions in CS50,","startTime":580410,"duration":2670,"startOfParagraph":false},{"content":"but if you do web development, you're going to run into this issue a lot.","startTime":583080,"duration":4840,"startOfParagraph":false},{"content":"I'm going to make it writable","startTime":587920,"duration":10420,"startOfParagraph":false},{"content":"and I think I also have to--there we go.","startTime":598340,"duration":5050,"startOfParagraph":false},{"content":"Okay, so now you can see everything passed,","startTime":603390,"duration":3650,"startOfParagraph":true},{"content":"and now it will tell you to rename the install.php file.","startTime":607040,"duration":2960,"startOfParagraph":false},{"content":"I'm going to move the install.php file to installed.php,","startTime":610000,"duration":5630,"startOfParagraph":false},{"content":"and now if I refresh,","startTime":615630,"duration":3980,"startOfParagraph":false},{"content":"it gives me some error, and this is where the debugging comes in.","startTime":619610,"duration":3200,"startOfParagraph":false},{"content":"This is where you can see what's actually going to happen.","startTime":622810,"duration":2800,"startOfParagraph":false},{"content":"The thing is, by default, Kohana assumes that","startTime":625610,"duration":2850,"startOfParagraph":false},{"content":"your project is at the root directory of your domain,","startTime":628460,"duration":3020,"startOfParagraph":false},{"content":"so it's expecting you to be at demo.brandonkliu.com.","startTime":631480,"duration":4440,"startOfParagraph":false},{"content":"We have to tell it that it's actually in a subfolder.","startTime":635920,"duration":1620,"startOfParagraph":false},{"content":"It's in a subfolder called CS50 Kohana.","startTime":637540,"duration":2280,"startOfParagraph":false},{"content":"The thing is, it's misinterpreting CS50-Kohana","startTime":639820,"duration":2820,"startOfParagraph":false},{"content":"as something else, which I'll explain to you in a second.","startTime":642640,"duration":3040,"startOfParagraph":false},{"content":"But I should tell you that's something that's to be expected.","startTime":645680,"duration":4230,"startOfParagraph":false},{"content":"What we're going to do is we're going to go into this folder called bootstrap.php,","startTime":649910,"duration":3790,"startOfParagraph":false},{"content":"which is the configuration folder where a lot of different things are set up.","startTime":653700,"duration":5560,"startOfParagraph":false},{"content":"I open that up.","startTime":659260,"duration":2310,"startOfParagraph":false},{"content":"Then maybe one of the first things I'll do is change the time zone.","startTime":661570,"duration":8220,"startOfParagraph":false},{"content":"And then let's see.","startTime":669790,"duration":4120,"startOfParagraph":true},{"content":"Aha! Right here.","startTime":673910,"duration":1270,"startOfParagraph":false},{"content":"There are a bunch of different configuration sayings in here,","startTime":675180,"duration":3510,"startOfParagraph":false},{"content":"but the one I'm looking for is this thing called base URL,","startTime":678690,"duration":3010,"startOfParagraph":false},{"content":"and by default I get it set to Kohana,","startTime":681700,"duration":2870,"startOfParagraph":false},{"content":"but I'm going to change that to CS50-Kohana,","startTime":684570,"duration":4450,"startOfParagraph":false},{"content":"and I think that should fix it.","startTime":689020,"duration":3860,"startOfParagraph":false},{"content":"Yes, great.","startTime":692880,"duration":1270,"startOfParagraph":false},{"content":"By default, to see that it's working, it says, \"Hello World.\"","startTime":694150,"duration":5380,"startOfParagraph":false},{"content":"Where did that come from? How did we get to Hello World?","startTime":699530,"duration":3400,"startOfParagraph":false},{"content":"Where exactly is the code that actually wrote that?","startTime":702930,"duration":4710,"startOfParagraph":false},{"content":"To understand that, I'll introduce this concept called routing.","startTime":707640,"duration":2600,"startOfParagraph":false},{"content":"Pretty much all web frameworks have the concept called routing,","startTime":710240,"duration":2350,"startOfParagraph":false},{"content":"which is the piece of the software that will map a certain URL","startTime":712590,"duration":4640,"startOfParagraph":false},{"content":"to a certain piece of code within your framework.","startTime":717230,"duration":4320,"startOfParagraph":false},{"content":"For example, if you have some URL and you go to some URL like foo.com/blog/all","startTime":721550,"duration":5960,"startOfParagraph":false},{"content":"then what the framework is going to do--or at least what Kohana is going to do--","startTime":727510,"duration":3660,"startOfParagraph":false},{"content":"is it's going to find a class called controller blog,","startTime":731170,"duration":4370,"startOfParagraph":false},{"content":"and it's going to run the function named action all.","startTime":735540,"duration":3180,"startOfParagraph":false},{"content":"I know I'm talking about class and functions,","startTime":738720,"duration":1440,"startOfParagraph":false},{"content":"and I know you guys haven't covered classes and functions","startTime":740160,"duration":3700,"startOfParagraph":false},{"content":"in CS50 yet, but for now,","startTime":743860,"duration":2610,"startOfParagraph":false},{"content":"you can think of classes as just a group of functions,","startTime":746470,"duration":3330,"startOfParagraph":false},{"content":"a way of grouping functions together.","startTime":749800,"duration":3100,"startOfParagraph":false},{"content":"That's really all you need to know.","startTime":752900,"duration":4790,"startOfParagraph":false},{"content":"Now if we look at our folder structure,","startTime":757690,"duration":5430,"startOfParagraph":true},{"content":"inside the application folder there is another folder called classes,","startTime":763120,"duration":3990,"startOfParagraph":false},{"content":"and the other folders are called Controller and Model.","startTime":767110,"duration":2090,"startOfParagraph":false},{"content":"If you look inside the Controller folder,","startTime":769200,"duration":3330,"startOfParagraph":false},{"content":"we see that there is a file called Welcome,","startTime":772530,"duration":3800,"startOfParagraph":false},{"content":"and you can see here is a class called Controller Welcome,","startTime":776330,"duration":4040,"startOfParagraph":false},{"content":"and there is a function called Action Index,","startTime":780370,"duration":1970,"startOfParagraph":false},{"content":"and what it does is it sets the body of your response to Hello World.","startTime":782340,"duration":4020,"startOfParagraph":false},{"content":"That's where the code is being written.","startTime":786360,"duration":2370,"startOfParagraph":false},{"content":"The other question is, well, I didn't go to ","startTime":788730,"duration":2880,"startOfParagraph":false},{"content":"blah, blah, blah, /welcome/index.","startTime":791610,"duration":2070,"startOfParagraph":false},{"content":"How did I end up here?","startTime":793680,"duration":2570,"startOfParagraph":false},{"content":"Well, that's simply because ","startTime":796250,"duration":4160,"startOfParagraph":false},{"content":"here at the bottom of our bootstrap file","startTime":800410,"duration":2000,"startOfParagraph":false},{"content":"where we set our routes","startTime":802410,"duration":2140,"startOfParagraph":false},{"content":"you can see that they set some defaults for you.","startTime":804550,"duration":2020,"startOfParagraph":false},{"content":"The default controller is Welcome. The default action is Index.","startTime":806570,"duration":2420,"startOfParagraph":false},{"content":"That's why when we put nothing in there it automatically went to the Welcome controller","startTime":808990,"duration":2610,"startOfParagraph":false},{"content":"and the index Action.","startTime":811600,"duration":2340,"startOfParagraph":false},{"content":"Everything make sense so far?","startTime":813940,"duration":3520,"startOfParagraph":true},{"content":"Now, you can do more than just go to Controller","startTime":817460,"duration":3390,"startOfParagraph":false},{"content":"and a specific action.","startTime":820850,"duration":2820,"startOfParagraph":false},{"content":"You can also pass in parameters to the controller.","startTime":823670,"duration":4810,"startOfParagraph":false},{"content":"Just as an example, ","startTime":828480,"duration":7910,"startOfParagraph":false},{"content":"I'm going to add another action to this controller to show you.","startTime":836390,"duration":7680,"startOfParagraph":false},{"content":"Let's call this action Echo, because it's going to tell you whatever you give it,","startTime":844070,"duration":5060,"startOfParagraph":false},{"content":"and so I'm basically going to grab","startTime":849130,"duration":8380,"startOfParagraph":false},{"content":"a parameter that's going to be sent through me to the routing program,","startTime":857510,"duration":3450,"startOfParagraph":false},{"content":"and as you can see here,","startTime":860960,"duration":3480,"startOfParagraph":false},{"content":"this line right here, you can see that","startTime":864440,"duration":4000,"startOfParagraph":false},{"content":"this basically means you have controller, and you have a /, ","startTime":868440,"duration":2830,"startOfParagraph":false},{"content":"and you have action, and you have another /,","startTime":871270,"duration":2210,"startOfParagraph":false},{"content":"and that's going to be parameters, and because we have this name ID","startTime":873480,"duration":5660,"startOfParagraph":false},{"content":"within angle brackets, that means that we're naming this parameter ID.","startTime":879140,"duration":3310,"startOfParagraph":false},{"content":"Later in my controller code if I want to grab a hold of that parameter,","startTime":882450,"duration":3040,"startOfParagraph":false},{"content":"I can use the code I wrote, find the parameter named ID.","startTime":885490,"duration":6300,"startOfParagraph":false},{"content":"That's what I did here, and I'm going to return and say, ","startTime":891790,"duration":7500,"startOfParagraph":false},{"content":"\"You said\" that.","startTime":899290,"duration":7800,"startOfParagraph":false},{"content":"And so now if I go to our website,","startTime":907090,"duration":3670,"startOfParagraph":false},{"content":"I go to cs50-kohana/welcome/echo/Helloooo--","startTime":910760,"duration":8790,"startOfParagraph":false},{"content":"oh, that's right.","startTime":919550,"duration":1870,"startOfParagraph":false},{"content":"There is 1 step I left out.","startTime":921420,"duration":2510,"startOfParagraph":false},{"content":"This is part of the live coding idea.","startTime":923930,"duration":3090,"startOfParagraph":false},{"content":"Here's 1 thing. Let's see.","startTime":927020,"duration":9080,"startOfParagraph":true},{"content":"So normally by default with a lot of these web applications","startTime":936100,"duration":2770,"startOfParagraph":false},{"content":"you have to include this index.php thing in your URL,","startTime":938870,"duration":3950,"startOfParagraph":false},{"content":"because the idea is index.php is sort of the entry point","startTime":942820,"duration":3590,"startOfParagraph":false},{"content":"of your application, but of course,","startTime":946410,"duration":1840,"startOfParagraph":false},{"content":"that's sort of annoying to have.","startTime":948250,"duration":1860,"startOfParagraph":false},{"content":"You don't want to have index.php appear in your URL,","startTime":950110,"duration":3680,"startOfParagraph":false},{"content":"and pretty much every web framework out of the box","startTime":953790,"duration":2290,"startOfParagraph":false},{"content":"has this index.php problem,","startTime":956080,"duration":2360,"startOfParagraph":false},{"content":"and so you have to take some measures to be able to remove that.","startTime":958440,"duration":4930,"startOfParagraph":false},{"content":"And so in this case,","startTime":963370,"duration":4170,"startOfParagraph":false},{"content":"what we're going to do is we're going to use a file called .htaccess,","startTime":967540,"duration":3910,"startOfParagraph":false},{"content":"and this is something that's specific to the Apache web server,","startTime":971450,"duration":2450,"startOfParagraph":false},{"content":"and it can do things like rewrite URLs","startTime":973900,"duration":2390,"startOfParagraph":false},{"content":"and redirect URLs and so on and so forth,","startTime":976290,"duration":3060,"startOfParagraph":false},{"content":"and Kohana is nice enough to provide a template .htaccess file that we can use.","startTime":979350,"duration":4930,"startOfParagraph":false},{"content":"As you can see, there is a file there called example.htaccess,","startTime":984280,"duration":4020,"startOfParagraph":true},{"content":"and we're going to copy that to .htaccess.","startTime":988300,"duration":5110,"startOfParagraph":false},{"content":"I'm going to open this and edit it,","startTime":993410,"duration":3540,"startOfParagraph":false},{"content":"and basically it does a bunch of different things.","startTime":996950,"duration":3890,"startOfParagraph":false},{"content":"The key line you may want to look at is right here.","startTime":1000840,"duration":4480,"startOfParagraph":false},{"content":"The idea is that this sets up a rule that says, ","startTime":1005320,"duration":4520,"startOfParagraph":false},{"content":"\"Okay, whatever you type in, prepend index.php to that.\"","startTime":1009840,"duration":6560,"startOfParagraph":false},{"content":"You can see that. ","startTime":1016400,"duration":2310,"startOfParagraph":false},{"content":"The .* stands for anything, match anything,","startTime":1018710,"duration":1660,"startOfParagraph":false},{"content":"and then the second part is index.php/$0,","startTime":1020370,"duration":2930,"startOfParagraph":false},{"content":"and $0 refers to whatever was matched previously.","startTime":1023300,"duration":4110,"startOfParagraph":false},{"content":"Does that make sense?","startTime":1027410,"duration":2090,"startOfParagraph":false},{"content":"But the really key thing I want to change is change this rewrite base,","startTime":1029500,"duration":2690,"startOfParagraph":false},{"content":"which is the URL base.","startTime":1032190,"duration":2110,"startOfParagraph":false},{"content":"It sort of assumes where you're working from.","startTime":1034300,"duration":3480,"startOfParagraph":false},{"content":"I'm going to add CS50 Kohana to that,","startTime":1037780,"duration":4780,"startOfParagraph":false},{"content":"and that way now if I remove the index.php,","startTime":1042560,"duration":3970,"startOfParagraph":false},{"content":"it should work, and I'm going to add some numbers","startTime":1046530,"duration":5580,"startOfParagraph":false},{"content":"to show you that it indeed did work.","startTime":1052110,"duration":4270,"startOfParagraph":false},{"content":"Sounds good.","startTime":1056380,"duration":1750,"startOfParagraph":false},{"content":"Any questions so far?","startTime":1058130,"duration":2130,"startOfParagraph":true},{"content":"[Student] How did it know to make the 123?","startTime":1060260,"duration":2040,"startOfParagraph":false},{"content":"Is that an argument?","startTime":1062300,"duration":1820,"startOfParagraph":false},{"content":"Exactly. You can think of it just like an argument.","startTime":1064120,"duration":2440,"startOfParagraph":false},{"content":"But the weird thing, though, is that the way Kohana does it","startTime":1066560,"duration":5850,"startOfParagraph":false},{"content":"is they don't do it exactly like an argument.","startTime":1072410,"duration":2500,"startOfParagraph":false},{"content":"You have to grab it like this.","startTime":1074910,"duration":2020,"startOfParagraph":false},{"content":"You have to grab the request object and ask for the parameter that's named ID,","startTime":1076930,"duration":4100,"startOfParagraph":false},{"content":"and that name ID comes from that bootstrap file","startTime":1081030,"duration":2210,"startOfParagraph":false},{"content":"that I showed earlier, and the name ID was in those angle brackets,","startTime":1083240,"duration":3750,"startOfParagraph":false},{"content":"and that's how you grab those parameters.","startTime":1086990,"duration":4590,"startOfParagraph":false},{"content":"Awesome.","startTime":1091580,"duration":2430,"startOfParagraph":false},{"content":"Any other questions?","startTime":1094010,"duration":3540,"startOfParagraph":false},{"content":"Like I said, controllers, they handle application logic,","startTime":1097550,"duration":2950,"startOfParagraph":false},{"content":"so that's 1 instance where you can see that's--","startTime":1100500,"duration":2480,"startOfParagraph":false},{"content":"it's very basic, but it's still application logic,","startTime":1102980,"duration":1850,"startOfParagraph":false},{"content":"the idea of grabbing the parameter and creating a new string","startTime":1104830,"duration":3150,"startOfParagraph":false},{"content":"that says, \"You said blah,\" and then spitting that back to you.","startTime":1107980,"duration":3940,"startOfParagraph":false},{"content":"And generally what you do is you create different controllers.","startTime":1111920,"duration":2110,"startOfParagraph":false},{"content":"You create separate controllers for different parts of your website.","startTime":1114030,"duration":2420,"startOfParagraph":false},{"content":"Today we're going to make a very simple website,","startTime":1116450,"duration":1710,"startOfParagraph":true},{"content":"and it's going to be a very basic blog.","startTime":1118160,"duration":2260,"startOfParagraph":false},{"content":"We're going to make a new controller just for the posts in a blog.","startTime":1120420,"duration":3360,"startOfParagraph":false},{"content":"But then if I were to also add comments to the blog post,","startTime":1123780,"duration":3280,"startOfParagraph":false},{"content":"then I would probably want to make a new controller for those comments.","startTime":1127060,"duration":3080,"startOfParagraph":false},{"content":"If I wanted to add users, I would probably add a new controller for those users,","startTime":1130140,"duration":3240,"startOfParagraph":false},{"content":"and in general, the idea is that whenever you have a new model,","startTime":1133380,"duration":3620,"startOfParagraph":false},{"content":"a new data object that you're dealing with,","startTime":1137000,"duration":2630,"startOfParagraph":false},{"content":"you have a single controller for that data object.","startTime":1139630,"duration":3340,"startOfParagraph":false},{"content":"Today we're only going to work with 1 data object, ","startTime":1142970,"duration":1400,"startOfParagraph":false},{"content":"and that's going to be posts, ","startTime":1144370,"duration":1880,"startOfParagraph":false},{"content":"and also you can think of data objects as corresponding to tables.","startTime":1146250,"duration":2460,"startOfParagraph":false},{"content":"Generally each table corresponds to 1 type of data object,","startTime":1148710,"duration":3450,"startOfParagraph":false},{"content":"so the post table will have 1 post model,","startTime":1152160,"duration":3000,"startOfParagraph":false},{"content":"which will have 1 post controller corresponding to that,","startTime":1155160,"duration":3070,"startOfParagraph":false},{"content":"and the same for comments, the same for users, and so on and so forth.","startTime":1158230,"duration":3960,"startOfParagraph":false},{"content":"And that's a general rule of thumb.","startTime":1162190,"duration":1880,"startOfParagraph":false},{"content":"There are going to be special cases where you may differ from that,","startTime":1164070,"duration":3390,"startOfParagraph":false},{"content":"but 90% of the time that's what you're going to be doing, ","startTime":1167460,"duration":1840,"startOfParagraph":false},{"content":"and I'll show you that's what we're going to be doing today.","startTime":1169300,"duration":3510,"startOfParagraph":false},{"content":"1 more concept before we dive back into the code,","startTime":1172810,"duration":2680,"startOfParagraph":false},{"content":"this idea of object relational mapping.","startTime":1175490,"duration":2220,"startOfParagraph":false},{"content":"You guys have already done a web development pset,","startTime":1177710,"duration":3490,"startOfParagraph":true},{"content":"and you've seen that you make an SQL query,","startTime":1181200,"duration":2620,"startOfParagraph":false},{"content":"and whatever it returns to you are rows.","startTime":1183820,"duration":2690,"startOfParagraph":false},{"content":"You get these rows, and you index them by some name,","startTime":1186510,"duration":3530,"startOfParagraph":false},{"content":"the name of the column and the table,","startTime":1190040,"duration":5440,"startOfParagraph":false},{"content":"and that's how you work with it,","startTime":1195480,"duration":2150,"startOfParagraph":false},{"content":"and it can be a bit cumbersome.","startTime":1197630,"duration":1660,"startOfParagraph":false},{"content":"But furthermore, if you have relationships within your database,","startTime":1199290,"duration":2520,"startOfParagraph":false},{"content":"like for example if I have comments and posts,","startTime":1201810,"duration":3470,"startOfParagraph":false},{"content":"then maybe I want to grab the parent post of a comment.","startTime":1205280,"duration":5960,"startOfParagraph":false},{"content":"If I use just rows in SQL, then all I can get is the ID","startTime":1211240,"duration":3110,"startOfParagraph":false},{"content":"of the parent post and not the actual post itself.","startTime":1214350,"duration":4960,"startOfParagraph":false},{"content":"But when we're coding, what we actually want is to actually grab","startTime":1219310,"duration":2370,"startOfParagraph":false},{"content":" the parent post itself sometimes.","startTime":1221680,"duration":1870,"startOfParagraph":false},{"content":"What object relational mapping does is ","startTime":1223550,"duration":2180,"startOfParagraph":false},{"content":"it takes the results of the database query","startTime":1225730,"duration":3750,"startOfParagraph":false},{"content":"and puts it into objects for you, which are much nicer to work with","startTime":1229480,"duration":2940,"startOfParagraph":false},{"content":"than plain arrays and rows.","startTime":1232420,"duration":2350,"startOfParagraph":false},{"content":"For example, now when I have a comment perhaps,","startTime":1234770,"duration":2780,"startOfParagraph":true},{"content":"and I want to grab its parent post,","startTime":1237550,"duration":3350,"startOfParagraph":false},{"content":"and I do maybe comment arrow post,","startTime":1240900,"duration":2540,"startOfParagraph":false},{"content":"then it will actually give me the post object ","startTime":1243440,"duration":1790,"startOfParagraph":false},{"content":"corresponding to the actual parent post, not just some ID,","startTime":1245230,"duration":2710,"startOfParagraph":false},{"content":"which I would otherwise have to use and make another SQL query to grab the post,","startTime":1247940,"duration":4270,"startOfParagraph":false},{"content":"which is cumbersome and unnecessary.","startTime":1252210,"duration":5220,"startOfParagraph":false},{"content":"And furthermore, by mapping all these data rows into objects,","startTime":1257430,"duration":4410,"startOfParagraph":false},{"content":"you can also attach more functions to objects,","startTime":1261840,"duration":1920,"startOfParagraph":false},{"content":"so for example, I talked about how classes are essentially groupings of functions. ","startTime":1263760,"duration":5940,"startOfParagraph":false},{"content":"You can think of it like that.","startTime":1269700,"duration":1920,"startOfParagraph":false},{"content":"For example, maybe I have this post object,","startTime":1271620,"duration":3670,"startOfParagraph":false},{"content":"and maybe I'd like to have some sort of function attached to it","startTime":1275290,"duration":2540,"startOfParagraph":false},{"content":"that basically tells me was it recently posted?","startTime":1277830,"duration":2470,"startOfParagraph":false},{"content":"Was it posted within the last week, true or false?","startTime":1280300,"duration":3270,"startOfParagraph":false},{"content":"And that's a function I can attach onto that object,","startTime":1283570,"duration":3750,"startOfParagraph":false},{"content":"and it's really convenient to have it in the same place,","startTime":1287320,"duration":3980,"startOfParagraph":false},{"content":"and there are a host of different functions you can create","startTime":1291300,"duration":2520,"startOfParagraph":false},{"content":"for these objects, and it's really nice to be able to attach it to a class,","startTime":1293820,"duration":4170,"startOfParagraph":false},{"content":"to an object, whereas if you just had rows coming from your database,","startTime":1297990,"duration":3710,"startOfParagraph":false},{"content":"then you can't really attach any functionality to that.","startTime":1301700,"duration":2090,"startOfParagraph":false},{"content":"It's literally just data.","startTime":1303790,"duration":4060,"startOfParagraph":false},{"content":"Any questions about that at all?","startTime":1307850,"duration":2700,"startOfParagraph":false},{"content":"ORMs are very common web development,","startTime":1310550,"duration":2160,"startOfParagraph":false},{"content":"and there are a lot of different types of ORMs,","startTime":1312710,"duration":3620,"startOfParagraph":false},{"content":"and Kohana has its own ORM.","startTime":1316330,"duration":2120,"startOfParagraph":false},{"content":"It's very basic, but you'll get a taste of what it looks like.","startTime":1318450,"duration":6600,"startOfParagraph":false},{"content":"Let's create a model for our blog posts,","startTime":1325050,"duration":3730,"startOfParagraph":true},{"content":"and the first thing we obviously need to do is to create an actual table","startTime":1328780,"duration":3570,"startOfParagraph":false},{"content":"within our database to actually store our data for those posts.","startTime":1332350,"duration":4330,"startOfParagraph":false},{"content":"The first thing I'm going to do is go to phpMyAdmin.","startTime":1336680,"duration":2580,"startOfParagraph":false},{"content":"Have you guys used phpMyAdmin before?","startTime":1339260,"duration":2150,"startOfParagraph":false},{"content":"Okay, awesome, so you guys already know what that is,","startTime":1341410,"duration":1990,"startOfParagraph":false},{"content":"and I'm going to create a new table called Kohana Posts,","startTime":1343400,"duration":8800,"startOfParagraph":false},{"content":"and it's going to be really simple.","startTime":1352200,"duration":5620,"startOfParagraph":false},{"content":"I'll have to log back in.","startTime":1357820,"duration":2370,"startOfParagraph":false},{"content":"All we're going to do today is have an author and a body,","startTime":1382620,"duration":2020,"startOfParagraph":false},{"content":"just keep it simple.","startTime":1384640,"duration":7290,"startOfParagraph":false},{"content":"I'm going to create that table,","startTime":1391930,"duration":3690,"startOfParagraph":false},{"content":"and now we just have a table representing our posts","startTime":1395620,"duration":4000,"startOfParagraph":false},{"content":"with 2 fields for our author and our body.","startTime":1399620,"duration":3750,"startOfParagraph":false},{"content":"The other thing I am going to do now is ","startTime":1403370,"duration":2920,"startOfParagraph":false},{"content":"configure my web application so it knows how to connect to the database,","startTime":1406290,"duration":3530,"startOfParagraph":false},{"content":"and this, again, is something that you'll have to do with all web applications.","startTime":1409820,"duration":2130,"startOfParagraph":false},{"content":"You have to tell it the user name and the password","startTime":1411950,"duration":2840,"startOfParagraph":false},{"content":"and the name of the database and so on and so forth","startTime":1414790,"duration":2200,"startOfParagraph":false},{"content":"to figure out how to actually connect to your database.","startTime":1416990,"duration":3010,"startOfParagraph":false},{"content":"In Kohana, we have something called a database module,","startTime":1420000,"duration":18710,"startOfParagraph":true},{"content":"and in the configuration folder we have this folder called Database,","startTime":1438710,"duration":3980,"startOfParagraph":false},{"content":"and as you can see, there are a bunch of settings you have to set here","startTime":1442690,"duration":4640,"startOfParagraph":false},{"content":"to tell it what's the user name and the password","startTime":1447330,"duration":2530,"startOfParagraph":false},{"content":"for the database so I can actually connect to it.","startTime":1449860,"duration":3250,"startOfParagraph":false},{"content":"And since I don't want you guys to actually know","startTime":1453110,"duration":1900,"startOfParagraph":false},{"content":"the user name and password of my database,","startTime":1455010,"duration":2180,"startOfParagraph":false},{"content":"I have a file where I already set it all up, and I'm going to copy and paste it over.","startTime":1457190,"duration":6650,"startOfParagraph":false},{"content":"Awesome.","startTime":1473080,"duration":3790,"startOfParagraph":false},{"content":"Okay. I think that's all the configuration I need to do,","startTime":1476870,"duration":3010,"startOfParagraph":false},{"content":"but let's see.","startTime":1479880,"duration":1190,"startOfParagraph":false},{"content":"We'll keep working in it, and if something crashes, ","startTime":1481070,"duration":2650,"startOfParagraph":false},{"content":"then we'll fix it.","startTime":1483720,"duration":3770,"startOfParagraph":false},{"content":"Now what I'm going to do is I'm going to create a new controller.","startTime":1487490,"duration":4340,"startOfParagraph":false},{"content":"Or actually, sorry.","startTime":1491830,"duration":1360,"startOfParagraph":false},{"content":"First I have to create a new model.","startTime":1493190,"duration":1890,"startOfParagraph":false},{"content":"I'll create a new model called Post.php,","startTime":1495080,"duration":6540,"startOfParagraph":false},{"content":"and what we're going to do is we're going to call it class Model_Post.","startTime":1501620,"duration":10820,"startOfParagraph":false},{"content":"Get some syntax highlighting on,","startTime":1512440,"duration":2950,"startOfParagraph":false},{"content":"and so when I say, \"extends ORM,\" that's basically ","startTime":1515390,"duration":4360,"startOfParagraph":false},{"content":"some more object-oriented programming,","startTime":1519750,"duration":1460,"startOfParagraph":false},{"content":"which unfortunately you guys haven't learned in CS50 yet, ","startTime":1521210,"duration":2130,"startOfParagraph":false},{"content":"but it's pretty easy to pick up.","startTime":1523340,"duration":1950,"startOfParagraph":false},{"content":"It gives me all this extra functionality that comes in this ORM package,","startTime":1525290,"duration":2660,"startOfParagraph":false},{"content":"and so I get a bunch of extra functions and whatnot for free,","startTime":1527950,"duration":3170,"startOfParagraph":false},{"content":"which you'll see a bit of in a second.","startTime":1531120,"duration":3690,"startOfParagraph":false},{"content":"Right now actually all I need to do is create this class.","startTime":1534810,"duration":2860,"startOfParagraph":true},{"content":"I don't even need to make any function or anything,","startTime":1537670,"duration":1490,"startOfParagraph":false},{"content":"but I've created a class that represents the table,","startTime":1539160,"duration":2610,"startOfParagraph":false},{"content":"and because I've extended this ORM class,","startTime":1541770,"duration":2370,"startOfParagraph":false},{"content":"I get a bunch of things for free, so for now you don't have to set anything more up.","startTime":1544140,"duration":6940,"startOfParagraph":false},{"content":"And now what I'm going to do is I'm going to create a new controller,","startTime":1551080,"duration":2450,"startOfParagraph":false},{"content":"which I'm going to name blog.php,","startTime":1553530,"duration":4950,"startOfParagraph":false},{"content":"and I'm going to copy over the Welcome controller","startTime":1558480,"duration":5870,"startOfParagraph":false},{"content":"so I don't have to retype some stuff,","startTime":1564350,"duration":7600,"startOfParagraph":false},{"content":"and now I have to rename this.","startTime":1571950,"duration":8770,"startOfParagraph":false},{"content":"Now what I'm going to do to test to make sure everything is working out,","startTime":1580720,"duration":3990,"startOfParagraph":false},{"content":"I'm going to grab the first post from my database","startTime":1584710,"duration":3110,"startOfParagraph":false},{"content":"and print the body of the post on the screen.","startTime":1587820,"duration":4860,"startOfParagraph":false},{"content":"To do that what I'm going to do first is I'm going to save the posts","startTime":1592680,"duration":5240,"startOfParagraph":false},{"content":"to a variable so what we're going to do is--","startTime":1597920,"duration":10850,"startOfParagraph":false},{"content":"in Kohana what you do is to grab the post object","startTime":1608770,"duration":3320,"startOfParagraph":false},{"content":"it's kind of cumbersome, but you have to do this thing called ORM:: factory,","startTime":1612090,"duration":3290,"startOfParagraph":false},{"content":"and then you pass in the name of the model you want,","startTime":1615380,"duration":2370,"startOfParagraph":false},{"content":"and it returns the ORM object that represents that model.","startTime":1617750,"duration":2740,"startOfParagraph":false},{"content":"And then, like I said, when we extend the ORM object,","startTime":1620490,"duration":4370,"startOfParagraph":false},{"content":"we get all these methods for free, so for example, ","startTime":1624860,"duration":2460,"startOfParagraph":false},{"content":"we get this new function called \"find all,\"","startTime":1627320,"duration":1880,"startOfParagraph":false},{"content":"which automatically returns every single post in the database,","startTime":1629200,"duration":2960,"startOfParagraph":false},{"content":"which is pretty convenient.","startTime":1632160,"duration":2690,"startOfParagraph":false},{"content":"And now in the body I'm going to return","startTime":1634850,"duration":2630,"startOfParagraph":true},{"content":"the first post and return its body.","startTime":1637480,"duration":7380,"startOfParagraph":false},{"content":"And of course, I need to create a post,","startTime":1644860,"duration":3070,"startOfParagraph":false},{"content":"so let's insert a new post.","startTime":1647930,"duration":3950,"startOfParagraph":false},{"content":"I'll say, \"Brandon, my very first post.\"","startTime":1651880,"duration":5990,"startOfParagraph":false},{"content":"Awesome.","startTime":1657870,"duration":2140,"startOfParagraph":false},{"content":"And now we're going to go to blogs","startTime":1660010,"duration":5900,"startOfParagraph":false},{"content":"and if all works well--oh, this is some other dumb file permission thing again.","startTime":1665910,"duration":5050,"startOfParagraph":false},{"content":"Hold on 1 second. It's kind of absurd.","startTime":1670960,"duration":5130,"startOfParagraph":false},{"content":"There we go. Okay.","startTime":1686700,"duration":1790,"startOfParagraph":false},{"content":"I fixed that permission problem.","startTime":1688490,"duration":1550,"startOfParagraph":false},{"content":"It was trying to create some files and some log,","startTime":1690040,"duration":2000,"startOfParagraph":false},{"content":"and the permissions, again, weren't properly set, so I made it","startTime":1692040,"duration":3360,"startOfParagraph":false},{"content":"so those files were writable and executable","startTime":1695400,"duration":2920,"startOfParagraph":false},{"content":"so it could actually log to things.","startTime":1698320,"duration":2770,"startOfParagraph":false},{"content":"Now it's giving me another exception saying, \"class ORM not found,\"","startTime":1701090,"duration":3130,"startOfParagraph":false},{"content":"and that's because I forgot another step.","startTime":1704220,"duration":2740,"startOfParagraph":false},{"content":"That's too bad.","startTime":1706960,"duration":10050,"startOfParagraph":false},{"content":"In the bootstrap folder file, there are these modules here,","startTime":1717010,"duration":3260,"startOfParagraph":false},{"content":"which you can choose to enable or disable.","startTime":1720270,"duration":2210,"startOfParagraph":false},{"content":"These are a bunch of different features that you can choose to use ","startTime":1722480,"duration":1860,"startOfParagraph":false},{"content":"within Kohana, which is sort of nice.","startTime":1724340,"duration":1840,"startOfParagraph":false},{"content":"For example, they have an authentication module","startTime":1726180,"duration":2910,"startOfParagraph":true},{"content":"which you can use for authenticating users.","startTime":1729090,"duration":2080,"startOfParagraph":false},{"content":"They have a caching module if you want to implement","startTime":1731170,"duration":2220,"startOfParagraph":false},{"content":"some sort of caching back end to make the application work faster and whatnot.","startTime":1733390,"duration":4480,"startOfParagraph":false},{"content":"We need to enable the database and the ORM module,","startTime":1737870,"duration":4270,"startOfParagraph":false},{"content":"because like I said, we're using the database, obviously,","startTime":1742140,"duration":2140,"startOfParagraph":false},{"content":"and we also need to enable the ORM module,","startTime":1744280,"duration":3920,"startOfParagraph":false},{"content":"because we'd like to have the extra functionality, which is nice to have.","startTime":1748200,"duration":4020,"startOfParagraph":false},{"content":"All I have to do is uncomment those 2 lines,","startTime":1752220,"duration":2020,"startOfParagraph":false},{"content":"and now if I refresh, it gave me another error.","startTime":1754240,"duration":4520,"startOfParagraph":false},{"content":"It says, \"Class Model_Post not found.\"","startTime":1758760,"duration":3340,"startOfParagraph":false},{"content":"Now this is a good problem to have.","startTime":1762100,"duration":8110,"startOfParagraph":false},{"content":"Let's see.","startTime":1770210,"duration":7450,"startOfParagraph":false},{"content":"Make it public.","startTime":1777660,"duration":4540,"startOfParagraph":false},{"content":"No. Hold on.","startTime":1782200,"duration":4250,"startOfParagraph":false},{"content":"Oh, dear.","startTime":1811610,"duration":1550,"startOfParagraph":false},{"content":"I do not know why it's not able to find that.","startTime":1813160,"duration":5430,"startOfParagraph":false},{"content":"That's really strange.","startTime":1818590,"duration":2440,"startOfParagraph":false},{"content":"I have this class right here.","startTime":1821030,"duration":2790,"startOfParagraph":false},{"content":"I guess I might have to--oh.","startTime":1823820,"duration":4830,"startOfParagraph":false},{"content":"I am so dumb. I forgot to add a PHP tag.","startTime":1828650,"duration":3360,"startOfParagraph":false},{"content":"That's why.","startTime":1832010,"duration":2660,"startOfParagraph":false},{"content":"Now I have to undo that 1 change I just did.","startTime":1834670,"duration":6590,"startOfParagraph":false},{"content":"Okay. There we go.","startTime":1841260,"duration":3010,"startOfParagraph":true},{"content":"That was really silly. I didn't have an opening PHP tag.","startTime":1844270,"duration":3230,"startOfParagraph":false},{"content":"But as you can see, now it's working properly, right?","startTime":1847500,"duration":2400,"startOfParagraph":false},{"content":"We have 1 post.","startTime":1849900,"duration":1340,"startOfParagraph":false},{"content":"We grabbed the first post, and now we printed out its body.","startTime":1851240,"duration":3490,"startOfParagraph":false},{"content":"Great. Fantastic.","startTime":1854730,"duration":3280,"startOfParagraph":false},{"content":"Any questions so far?","startTime":1858010,"duration":3460,"startOfParagraph":false},{"content":"Nope? Any questions?","startTime":1861470,"duration":2630,"startOfParagraph":false},{"content":"Okay, so we just created the post model, very basic,","startTime":1864100,"duration":4240,"startOfParagraph":false},{"content":"and we're going to add some functions later on.","startTime":1868340,"duration":2590,"startOfParagraph":false},{"content":"We can add validations and filtering.","startTime":1870930,"duration":2670,"startOfParagraph":false},{"content":"Validations are one of the things","startTime":1873600,"duration":2050,"startOfParagraph":false},{"content":"that frameworks solve for you really, really well,","startTime":1875650,"duration":2500,"startOfParagraph":false},{"content":"and I don't think you guys had to do this for your CS50 pset,","startTime":1878150,"duration":3160,"startOfParagraph":false},{"content":"but if you do web development for your final project,","startTime":1881310,"duration":2690,"startOfParagraph":false},{"content":"you're likely going to want to do some sort of validation,","startTime":1884000,"duration":2280,"startOfParagraph":false},{"content":"like not having blank user names,","startTime":1886280,"duration":2010,"startOfParagraph":false},{"content":"maybe having a password with at least some length, things like that.","startTime":1888290,"duration":3660,"startOfParagraph":false},{"content":"And it's really cumbersome to implement these things by ourselves,","startTime":1891950,"duration":2800,"startOfParagraph":false},{"content":"and pretty much every single web framework does it for you","startTime":1894750,"duration":2640,"startOfParagraph":false},{"content":"and allows you to do it in a very clean way.","startTime":1897390,"duration":3750,"startOfParagraph":false},{"content":"And the model is where you generally express those validation rules,","startTime":1901140,"duration":3200,"startOfParagraph":false},{"content":"because it's validating whether a model is valid or not.","startTime":1904340,"duration":4450,"startOfParagraph":false},{"content":"But for now, we're going to put that until later,","startTime":1908790,"duration":2560,"startOfParagraph":true},{"content":"and for now we're going to work on another part,","startTime":1911350,"duration":2170,"startOfParagraph":false},{"content":"and we're going to try and make a new view","startTime":1913520,"duration":1880,"startOfParagraph":false},{"content":"that lists all the posts.","startTime":1915400,"duration":4180,"startOfParagraph":false},{"content":"The steps involved in making a new action for listing all the posts","startTime":1919580,"duration":2910,"startOfParagraph":false},{"content":"is to grab a list of all the posts","startTime":1922490,"duration":2320,"startOfParagraph":false},{"content":"and then render the list of all the posts through a view.","startTime":1924810,"duration":7180,"startOfParagraph":false},{"content":"Right here, fortunately enough, we already grabbed all the posts","startTime":1931990,"duration":4430,"startOfParagraph":false},{"content":"using this first line, the find all function,","startTime":1936420,"duration":3890,"startOfParagraph":false},{"content":"and now what we're going to do is so far ","startTime":1940310,"duration":2210,"startOfParagraph":false},{"content":"I've been directly setting the body of the response","startTime":1942520,"duration":2830,"startOfParagraph":false},{"content":"by passing the string, but now I want to use a view,","startTime":1945350,"duration":3740,"startOfParagraph":false},{"content":"and the difference between a view and just doing this","startTime":1949090,"duration":2780,"startOfParagraph":false},{"content":"is with a view I can have a nice, big HTML template,","startTime":1951870,"duration":3460,"startOfParagraph":false},{"content":"and what I can do is pass it certain variables","startTime":1955330,"duration":2380,"startOfParagraph":false},{"content":"and then have the view automatically populate its template","startTime":1957710,"duration":4490,"startOfParagraph":false},{"content":"using those variables.","startTime":1962200,"duration":2490,"startOfParagraph":false},{"content":"What I'll do is I'll create a new view,","startTime":1964690,"duration":6090,"startOfParagraph":false},{"content":"and I'll name the view something like \"blog/index,\"","startTime":1970780,"duration":5160,"startOfParagraph":false},{"content":"and I'm going to basically bind this--oh, what am I writing?","startTime":1975940,"duration":12540,"startOfParagraph":false},{"content":"My brain is somewhere else.","startTime":1988480,"duration":4430,"startOfParagraph":false},{"content":"I'm going to bind the posts variable to the view,","startTime":1992910,"duration":3690,"startOfParagraph":false},{"content":"so that way the view has access to this post variable.","startTime":1996600,"duration":3350,"startOfParagraph":false},{"content":"And so now I need to create this view,","startTime":1999950,"duration":6190,"startOfParagraph":true},{"content":"so here we have this folder called \"Views,\"","startTime":2006140,"duration":2360,"startOfParagraph":false},{"content":"and first, I'm going to create a new folder under that called \"Blog.\"","startTime":2008500,"duration":3650,"startOfParagraph":false},{"content":"This is nice. That way we can have a nice hierarchy for our views.","startTime":2012150,"duration":3660,"startOfParagraph":false},{"content":"And then I'm going to create another file in there called \"index.php.\"","startTime":2015810,"duration":8100,"startOfParagraph":false},{"content":"Awesome.","startTime":2023910,"duration":1870,"startOfParagraph":false},{"content":"Actually, let's have them both here.","startTime":2025780,"duration":7150,"startOfParagraph":false},{"content":"Making a view file is probably the simplest part of all this,","startTime":2032930,"duration":3830,"startOfParagraph":false},{"content":"and these are probably things you're already familiar with.","startTime":2036760,"duration":2330,"startOfParagraph":false},{"content":"We're going to do something really simple,","startTime":2039090,"duration":2150,"startOfParagraph":false},{"content":"start saying, \"My list of blog posts.\"","startTime":2041240,"duration":4120,"startOfParagraph":false},{"content":"Then we can go through,","startTime":2045360,"duration":9500,"startOfParagraph":false},{"content":"and we can iterate through the posts array,","startTime":2054860,"duration":3060,"startOfParagraph":false},{"content":"grab every single post and say something like--","startTime":2057920,"duration":3840,"startOfParagraph":false},{"content":"maybe add a line","startTime":2061760,"duration":3530,"startOfParagraph":false},{"content":"and then print out the author and the body.","startTime":2065290,"duration":17170,"startOfParagraph":false},{"content":"That make sense so far?","startTime":2082460,"duration":2020,"startOfParagraph":false},{"content":"And let's see if it works.","startTime":2084480,"duration":6390,"startOfParagraph":false},{"content":"Nothing happened.","startTime":2090870,"duration":2619,"startOfParagraph":false},{"content":"I wonder why.","startTime":2093489,"duration":1601,"startOfParagraph":false},{"content":"Oh, I missed 1 step. Very silly of me.","startTime":2095090,"duration":3670,"startOfParagraph":false},{"content":"I created a view, but I didn't set the view as the response,","startTime":2098760,"duration":2880,"startOfParagraph":false},{"content":"so you have to do 1 more thing.","startTime":2101640,"duration":1550,"startOfParagraph":false},{"content":"You have to do \"this response body\" and set it to be the view.","startTime":2103190,"duration":9420,"startOfParagraph":false},{"content":"There we go.","startTime":2112610,"duration":2150,"startOfParagraph":false},{"content":"We have our heading, and then we have a post,","startTime":2114760,"duration":2440,"startOfParagraph":false},{"content":"and just for kicks, let's insert another post","startTime":2117200,"duration":3300,"startOfParagraph":false},{"content":"so we can see a list.","startTime":2120500,"duration":2890,"startOfParagraph":false},{"content":"And insert these 2 posts,","startTime":2131800,"duration":4850,"startOfParagraph":false},{"content":"and now if I refresh the page,","startTime":2136650,"duration":2850,"startOfParagraph":false},{"content":"we see all these posts here.","startTime":2139500,"duration":2560,"startOfParagraph":false},{"content":"Does that make sense so far?","startTime":2142060,"duration":2190,"startOfParagraph":true},{"content":"Yeah, a question? Oh, okay. ","startTime":2144250,"duration":2150,"startOfParagraph":false},{"content":"As you can see, we've been able to separate all these codes out","startTime":2146400,"duration":5040,"startOfParagraph":false},{"content":"into different sections, and then you can see it's most clear with the view code.","startTime":2151440,"duration":2480,"startOfParagraph":false},{"content":"This file here that represents the view,","startTime":2153920,"duration":3890,"startOfParagraph":false},{"content":"it only cares about representing data, displaying data.","startTime":2157810,"duration":3410,"startOfParagraph":false},{"content":"It gets passed some sort of data, and all it does is just show it to you.","startTime":2161220,"duration":3090,"startOfParagraph":false},{"content":"In all other parts of your code, you won't have to worry about any of that,","startTime":2164310,"duration":3350,"startOfParagraph":false},{"content":"and similarly, your view code doesn't have to worry anything about","startTime":2167660,"duration":2820,"startOfParagraph":false},{"content":"how to access the database and so on and so forth,","startTime":2170480,"duration":2910,"startOfParagraph":false},{"content":"which is really good and makes your code a lot more maintainable.","startTime":2173390,"duration":6560,"startOfParagraph":false},{"content":"Like I said, views, they're dynamic in that ","startTime":2179950,"duration":3440,"startOfParagraph":false},{"content":"it's 1 file, but it would generate different views","startTime":2183390,"duration":3690,"startOfParagraph":false},{"content":"based on the variables you actually pass in,","startTime":2187080,"duration":2860,"startOfParagraph":false},{"content":"and furthermore, there are a lot of different helper functions","startTime":2189940,"duration":2430,"startOfParagraph":false},{"content":"that you can use to help you write your code faster,","startTime":2192370,"duration":1860,"startOfParagraph":false},{"content":"which I'll show you in just a second.","startTime":2194230,"duration":2090,"startOfParagraph":false},{"content":"Yeah.","startTime":2196320,"duration":1730,"startOfParagraph":false},{"content":"[Student] So $0 is a controller, right?","startTime":2198050,"duration":4440,"startOfParagraph":true},{"content":"That second thing.","startTime":2202490,"duration":1510,"startOfParagraph":false},{"content":"The question is is $0 a controller?","startTime":2204000,"duration":2090,"startOfParagraph":false},{"content":"$0 is a variable I created right here.","startTime":2206090,"duration":2520,"startOfParagraph":false},{"content":"I created a view first. I assigned it to some variable.","startTime":2208610,"duration":2710,"startOfParagraph":false},{"content":"Then I passed it into this function, set it as the body of the response.","startTime":2211320,"duration":3640,"startOfParagraph":false},{"content":"Does that make sense?","startTime":2214960,"duration":2300,"startOfParagraph":false},{"content":"[Student] So is view :: factory, is view like a class","startTime":2217260,"duration":4940,"startOfParagraph":false},{"content":"or a library [inaudible] factory function?","startTime":2222200,"duration":4410,"startOfParagraph":false},{"content":"The question is about the view :: factory function, ","startTime":2226610,"duration":4030,"startOfParagraph":false},{"content":"and basically this is some more object-oriented programming essentially.","startTime":2230640,"duration":3380,"startOfParagraph":false},{"content":"View is the view class, and it has a method called \"Factory,\"","startTime":2234020,"duration":3980,"startOfParagraph":false},{"content":"and that's a way to grab the object that's named \"blog/index.\"","startTime":2238000,"duration":6170,"startOfParagraph":false},{"content":"And that's some more object-oriented programming stuff","startTime":2244170,"duration":2970,"startOfParagraph":false},{"content":"that I'm not going to go into here too much.","startTime":2247140,"duration":5870,"startOfParagraph":false},{"content":"Now obviously, we want to create new posts,","startTime":2253010,"duration":3390,"startOfParagraph":false},{"content":"but we don't want to have to do it through a database,","startTime":2256400,"duration":2390,"startOfParagraph":false},{"content":"so we're going to create a new action for creating a new post,","startTime":2258790,"duration":2490,"startOfParagraph":false},{"content":"and there is a lot of stuff we have to do.","startTime":2261280,"duration":1770,"startOfParagraph":false},{"content":"The first thing we're going to do--let's tackle these things one by one.","startTime":2263050,"duration":2860,"startOfParagraph":true},{"content":"The first thing we'll do is we've got to create a form","startTime":2265910,"duration":2410,"startOfParagraph":false},{"content":"for inserting a new post,","startTime":2268320,"duration":6140,"startOfParagraph":false},{"content":"but I'm also going to add a new action first,","startTime":2274460,"duration":2900,"startOfParagraph":false},{"content":"so adding a new action is just as easy as ","startTime":2277360,"duration":3690,"startOfParagraph":false},{"content":"adding a new function with your controller,","startTime":2281050,"duration":2440,"startOfParagraph":false},{"content":"and for now I'm going to do something very basic,","startTime":2283490,"duration":10220,"startOfParagraph":false},{"content":"just grab this view and post it, just display it for you.","startTime":2293710,"duration":7140,"startOfParagraph":false},{"content":"And then now I'm going to create a new view file,","startTime":2300850,"duration":5370,"startOfParagraph":false},{"content":"and I'm going to start writing some stuff.","startTime":2306220,"duration":7470,"startOfParagraph":false},{"content":"What's nice about Kohana is that they provide a lot of different helper functions","startTime":2313690,"duration":2850,"startOfParagraph":false},{"content":"for you to write view code more easily,","startTime":2316540,"duration":2250,"startOfParagraph":false},{"content":"and 1 of those helper functions or helper modules","startTime":2318790,"duration":3180,"startOfParagraph":false},{"content":"is around writing forms.","startTime":2321970,"duration":3890,"startOfParagraph":false},{"content":"For writing forms, I don't really have to directly write any HTML myself.","startTime":2325860,"duration":3600,"startOfParagraph":false},{"content":"You guys have written HTML forms.","startTime":2329460,"duration":1640,"startOfParagraph":false},{"content":"You know how it can be really, really painful and cumbersome to write forms.","startTime":2331100,"duration":3750,"startOfParagraph":false},{"content":"It's not fun, so fortunately,","startTime":2334850,"duration":5120,"startOfParagraph":false},{"content":"we can basically write a form using Kohana's ","startTime":2339970,"duration":4890,"startOfParagraph":false},{"content":"form helper functions to do it for us.","startTime":2344860,"duration":6330,"startOfParagraph":false},{"content":"We're going to basically have fields for every single thing we have,","startTime":2351190,"duration":6150,"startOfParagraph":false},{"content":"so one for authors and one for the bodies.","startTime":2357340,"duration":5820,"startOfParagraph":false},{"content":"We're going to have a label, and we're going to have an input.","startTime":2363160,"duration":3930,"startOfParagraph":false},{"content":"And then finally, we're going to have a submission.","startTime":2377450,"duration":3910,"startOfParagraph":true},{"content":"And as you can see, this is much cleaner to write","startTime":2389350,"duration":2880,"startOfParagraph":false},{"content":"than all that messy HTML, which is kind of nice.","startTime":2392230,"duration":5920,"startOfParagraph":false},{"content":"Granted, there are other web frameworks that have it even cleaner than that,","startTime":2398150,"duration":2780,"startOfParagraph":false},{"content":"but at least this is better than writing the HTML yourself.","startTime":2400930,"duration":3510,"startOfParagraph":false},{"content":"Awesome, so this is what you see.","startTime":2409400,"duration":1730,"startOfParagraph":false},{"content":"That's kind of messy,","startTime":2411130,"duration":2400,"startOfParagraph":false},{"content":"so I'm going to add a line break there","startTime":2413530,"duration":6190,"startOfParagraph":false},{"content":"to make that look a little nicer.","startTime":2419720,"duration":1460,"startOfParagraph":false},{"content":"Well, of course, it still looks really, really bad, but we're just focused","startTime":2421180,"duration":2150,"startOfParagraph":false},{"content":"on the functionality for now and not on the aesthetics.","startTime":2423330,"duration":2720,"startOfParagraph":false},{"content":"No time to do everything.","startTime":2426050,"duration":1960,"startOfParagraph":false},{"content":"And as you can see, now we have a super basic form,","startTime":2428010,"duration":2590,"startOfParagraph":false},{"content":"which is kind of nice.","startTime":2430600,"duration":1480,"startOfParagraph":false},{"content":"This code I would say is cleaner than trying to write an HTML form yourself,","startTime":2432080,"duration":4650,"startOfParagraph":false},{"content":"so that's nice.","startTime":2436730,"duration":3560,"startOfParagraph":false},{"content":"What's next?","startTime":2440290,"duration":1740,"startOfParagraph":false},{"content":"Now we need to do things with the action.","startTime":2442030,"duration":7230,"startOfParagraph":false},{"content":"Normally when you write HTML forms,","startTime":2449260,"duration":1980,"startOfParagraph":false},{"content":"you have to tell it where it's going to submit the form to.","startTime":2451240,"duration":2830,"startOfParagraph":false},{"content":"By default in most web frameworks,","startTime":2454070,"duration":1980,"startOfParagraph":false},{"content":"it submits to the exact same URL, so the thing is,","startTime":2456050,"duration":2150,"startOfParagraph":false},{"content":"if you send a get request to /blog/new,","startTime":2458200,"duration":3110,"startOfParagraph":false},{"content":"it should display you the form,","startTime":2461310,"duration":1930,"startOfParagraph":false},{"content":"but if you send a post request to /blog/new with the data,","startTime":2463240,"duration":3570,"startOfParagraph":false},{"content":"it should actually try to save that post","startTime":2466810,"duration":3190,"startOfParagraph":false},{"content":"and do something with it.","startTime":2470000,"duration":3300,"startOfParagraph":false},{"content":"What we're going to do is","startTime":2480630,"duration":1550,"startOfParagraph":true},{"content":"basically all we have to do to check whether it's a post request or a get request","startTime":2482180,"duration":3140,"startOfParagraph":false},{"content":"is to check what are the post variables you can set.","startTime":2485320,"duration":4030,"startOfParagraph":false},{"content":"And if the post variable is set, then we're going to try and create a new post.","startTime":2489350,"duration":5210,"startOfParagraph":false},{"content":"Again, we just do this,","startTime":2494560,"duration":3880,"startOfParagraph":false},{"content":"and that creates a new post, and we're literally going to","startTime":2498440,"duration":2650,"startOfParagraph":false},{"content":"set its fields like this,","startTime":2501090,"duration":10060,"startOfParagraph":false},{"content":"and then we're going to save it.","startTime":2511150,"duration":4490,"startOfParagraph":false},{"content":"And then I'm going to redirect","startTime":2515640,"duration":3560,"startOfParagraph":false},{"content":"to the index page so they can see our list of posts again.","startTime":2519200,"duration":8460,"startOfParagraph":false},{"content":"Let's try that.","startTime":2527660,"duration":1960,"startOfParagraph":false},{"content":"I'll say, \"Brandon,\"","startTime":2529620,"duration":5540,"startOfParagraph":false},{"content":"and then submit the post, and if all goes well,","startTime":2535160,"duration":2980,"startOfParagraph":false},{"content":"as you can see, it redirected me to the index page,","startTime":2538140,"duration":3250,"startOfParagraph":false},{"content":"and if I scroll to the bottom, we have a newly inserted post.","startTime":2541390,"duration":2750,"startOfParagraph":false},{"content":"Yay!","startTime":2544140,"duration":2290,"startOfParagraph":false},{"content":"Yeah, question.","startTime":2546430,"duration":2000,"startOfParagraph":false},{"content":"[Student] What if you had entered the exact same thing","startTime":2548430,"duration":3330,"startOfParagraph":true},{"content":"you entered before?","startTime":2551760,"duration":1620,"startOfParagraph":false},{"content":"Does it check to make sure you haven't duplicated","startTime":2553380,"duration":3570,"startOfParagraph":false},{"content":"the same submission?","startTime":2556950,"duration":1860,"startOfParagraph":false},{"content":"Be default, no, because by default--","startTime":2558810,"duration":2850,"startOfParagraph":false},{"content":"sorry, the question is if you enter in the exact same data in the form","startTime":2561660,"duration":4810,"startOfParagraph":false},{"content":"and submit that, will it allow you to insert a duplicate object,","startTime":2566470,"duration":3710,"startOfParagraph":false},{"content":"a duplicate entry, essentially?","startTime":2570180,"duration":2370,"startOfParagraph":false},{"content":"Right now, yes, it will allow you to do that,","startTime":2572550,"duration":1520,"startOfParagraph":false},{"content":"because in databases it's perfectly valid to have completely duplicate rows,","startTime":2574070,"duration":4790,"startOfParagraph":false},{"content":"but if that is a concern, then you can add validations, for example, ","startTime":2578860,"duration":3400,"startOfParagraph":false},{"content":"to make sure that if this is exactly the same as something that already exists,","startTime":2582260,"duration":4170,"startOfParagraph":false},{"content":"then say that it's an invalid object,","startTime":2586430,"duration":2290,"startOfParagraph":false},{"content":"and then you can even specify your error message","startTime":2588720,"duration":2480,"startOfParagraph":false},{"content":"and say, \"Invalid because this already exists\" or something like that.","startTime":2591200,"duration":3190,"startOfParagraph":false},{"content":"But in this case, I could just create something duplicate.","startTime":2594390,"duration":8030,"startOfParagraph":false},{"content":"Now let's try and add some validations.","startTime":2602420,"duration":3590,"startOfParagraph":false},{"content":"The problem with this right now is that ","startTime":2606010,"duration":4390,"startOfParagraph":false},{"content":"I could literally submit a completely blank post.","startTime":2610400,"duration":3820,"startOfParagraph":false},{"content":"I can click this button right now, and there we go.","startTime":2614220,"duration":3280,"startOfParagraph":false},{"content":"You can't really see it, but this extra line here","startTime":2617500,"duration":2790,"startOfParagraph":false},{"content":"indicates that I literally have a new post.","startTime":2620290,"duration":3540,"startOfParagraph":false},{"content":"It just has a blank author and a blank body,","startTime":2623830,"duration":2220,"startOfParagraph":false},{"content":"and we don't want to allow people to do that.","startTime":2626050,"duration":2580,"startOfParagraph":false},{"content":"This is where validation comes in.","startTime":2628630,"duration":3920,"startOfParagraph":false},{"content":"I can go to my model object, ","startTime":2632550,"duration":4990,"startOfParagraph":true},{"content":"and now I can add a new function that specifies","startTime":2637540,"duration":1990,"startOfParagraph":false},{"content":"what validation rules I should add to this model","startTime":2639530,"duration":2470,"startOfParagraph":false},{"content":"to make sure that it is valid or to specify what does it mean to be a valid post?","startTime":2642000,"duration":4840,"startOfParagraph":false},{"content":"And I want to say it's only a valid post if both the author and body","startTime":2646840,"duration":3370,"startOfParagraph":false},{"content":"are not blank, and this is how you do it in Kohana.","startTime":2650210,"duration":4940,"startOfParagraph":false},{"content":"You create a new function called \"Rules,\"","startTime":2655150,"duration":3600,"startOfParagraph":false},{"content":"and then you basically return an associative array","startTime":2658750,"duration":1460,"startOfParagraph":false},{"content":"that defines the validation rules for this object.","startTime":2660210,"duration":4020,"startOfParagraph":false},{"content":"We're going to return the array, and then what we're going to do is","startTime":2664230,"duration":3300,"startOfParagraph":false},{"content":"say \"author,\" it goes to an array,","startTime":2667530,"duration":5290,"startOfParagraph":false},{"content":"which goes to another array called \"not empty.\"","startTime":2672820,"duration":4900,"startOfParagraph":false},{"content":"And then I'm going to say \"body.\"","startTime":2677720,"duration":3760,"startOfParagraph":false},{"content":"Okay, and the syntax for this and structure for this","startTime":2690980,"duration":3140,"startOfParagraph":false},{"content":"may look a little cumbersome and a little complicated.","startTime":2694120,"duration":2410,"startOfParagraph":false},{"content":"If you read the documentation, it's pretty straightforward to figure out,","startTime":2696530,"duration":2800,"startOfParagraph":false},{"content":"But essentially this is what you need to do to specify","startTime":2699330,"duration":3170,"startOfParagraph":false},{"content":"some validation rules, and there are a lot of different rules","startTime":2702500,"duration":1630,"startOfParagraph":false},{"content":"that Kohana will give you for free, like you can add rules to say","startTime":2704130,"duration":2680,"startOfParagraph":false},{"content":"it must be at least this length.","startTime":2706810,"duration":1600,"startOfParagraph":false},{"content":"Maybe it has to be numeric. Maybe it has to be alpha numeric.","startTime":2708410,"duration":3390,"startOfParagraph":false},{"content":"Maybe it has to be at most this length, so on and so forth.","startTime":2711800,"duration":2610,"startOfParagraph":false},{"content":"There are a lot of different rules that Kohana provides for you,","startTime":2714410,"duration":3320,"startOfParagraph":false},{"content":"and you can go on their website, look at the documentation,","startTime":2717730,"duration":1880,"startOfParagraph":false},{"content":"and you can see all the different things that you can do.","startTime":2719610,"duration":3540,"startOfParagraph":false},{"content":"But this is all I have to do,","startTime":2723150,"duration":2500,"startOfParagraph":true},{"content":"and now let's see what happens","startTime":2725650,"duration":4840,"startOfParagraph":false},{"content":"if I submit a blank post.","startTime":2730490,"duration":3570,"startOfParagraph":false},{"content":"What's going to happen? Oh, no, I get an error.","startTime":2734060,"duration":2900,"startOfParagraph":false},{"content":"I get a validation exception.","startTime":2736960,"duration":2480,"startOfParagraph":false},{"content":"Well, it's good.","startTime":2739440,"duration":1630,"startOfParagraph":false},{"content":"It told me that my model is invalid,","startTime":2741070,"duration":2130,"startOfParagraph":false},{"content":"but I don't want to display an exception","startTime":2743200,"duration":2580,"startOfParagraph":false},{"content":"to my users when they try to submit something invalid, right?","startTime":2745780,"duration":2940,"startOfParagraph":false},{"content":"I want to give them some sort of friendlier error message","startTime":2748720,"duration":2840,"startOfParagraph":false},{"content":"when something goes wrong.","startTime":2751560,"duration":2050,"startOfParagraph":false},{"content":"What we're going to do is we're going to ","startTime":2753610,"duration":8220,"startOfParagraph":false},{"content":"wrap everything in a try catch loop.","startTime":2761830,"duration":2660,"startOfParagraph":false},{"content":"Actually, I think this is also something you have not learned yet","startTime":2764490,"duration":2260,"startOfParagraph":false},{"content":"in CS50, because C, the programming language C,","startTime":2766750,"duration":4070,"startOfParagraph":false},{"content":"doesn't have exceptions, but almost every single other language","startTime":2770820,"duration":3180,"startOfParagraph":false},{"content":"has exceptions, so really, really briefly,","startTime":2774000,"duration":2700,"startOfParagraph":false},{"content":"an exception is something that a piece of code can ","startTime":2776700,"duration":2730,"startOfParagraph":false},{"content":"throw an exception when something goes wrong,","startTime":2779430,"duration":2000,"startOfParagraph":false},{"content":"but then maybe some other piece of code higher up","startTime":2781430,"duration":1980,"startOfParagraph":false},{"content":"can catch that exception and do something with it.","startTime":2783410,"duration":2400,"startOfParagraph":false},{"content":"For example, in this case,","startTime":2785810,"duration":1900,"startOfParagraph":true},{"content":"the piece of code that's trying to save a model,","startTime":2787710,"duration":2230,"startOfParagraph":false},{"content":"it validates the model, and if it says, \"Okay, this model is invalid,\"","startTime":2789940,"duration":3230,"startOfParagraph":false},{"content":"it's going to throw an exception, and this is kind of equivalent to","startTime":2793170,"duration":2980,"startOfParagraph":false},{"content":"in C you might return a -1 or something like that.","startTime":2796150,"duration":3720,"startOfParagraph":false},{"content":"And then for me, this function, my code","startTime":2799870,"duration":2450,"startOfParagraph":false},{"content":"at a higher level, I can try and catch that exception","startTime":2802320,"duration":3990,"startOfParagraph":false},{"content":"and basically say, \"Okay, if I catch the exception, what am I going to do?\"","startTime":2806310,"duration":3020,"startOfParagraph":false},{"content":"Or I could choose not to catch that exception and let someone higher up","startTime":2809330,"duration":2240,"startOfParagraph":false},{"content":"catch the exception, or if nobody catches it,","startTime":2811570,"duration":2830,"startOfParagraph":false},{"content":"then the whole program crashes and says,","startTime":2814400,"duration":2420,"startOfParagraph":false},{"content":"\"Something went wrong, and I couldn't handle it.\"","startTime":2816820,"duration":2350,"startOfParagraph":false},{"content":"But what we do is you wrap a piece of code in a try block,","startTime":2819170,"duration":5320,"startOfParagraph":true},{"content":"and then you also add something called a catch block,","startTime":2824490,"duration":4540,"startOfParagraph":false},{"content":"which is the sort of code that will try and catch exceptions that may occur.","startTime":2829030,"duration":8270,"startOfParagraph":false},{"content":"And so if I catch this particular exception","startTime":2837300,"duration":3130,"startOfParagraph":false},{"content":"or invalidation exception, then what I'm going to do is ","startTime":2840430,"duration":2680,"startOfParagraph":false},{"content":"I'm going to set the errors--I think that's how I do it--","startTime":2843110,"duration":8100,"startOfParagraph":false},{"content":"and I'm going to set the errors to some object.","startTime":2851210,"duration":4160,"startOfParagraph":false},{"content":"And then what I'm going to do is if it hits this exception,","startTime":2855370,"duration":5550,"startOfParagraph":false},{"content":"it's not going to redirect, and if it doesn't redirect,","startTime":2860920,"duration":2170,"startOfParagraph":false},{"content":"it's going to come out of the if blog","startTime":2863090,"duration":3070,"startOfParagraph":false},{"content":"and hit this blog/new, which is want I want to do.","startTime":2866160,"duration":3760,"startOfParagraph":false},{"content":"If there is an error, then I want to go back to the form","startTime":2869920,"duration":3270,"startOfParagraph":false},{"content":"and display those errors.","startTime":2873190,"duration":1910,"startOfParagraph":false},{"content":"Now what I want to do is I want to pass in those errors","startTime":2875100,"duration":5680,"startOfParagraph":false},{"content":"to the view.","startTime":2880780,"duration":6230,"startOfParagraph":false},{"content":"Okay, I think I have the view right here,","startTime":2887010,"duration":3350,"startOfParagraph":false},{"content":"and basically I want to display those errors if they exist.","startTime":2890360,"duration":4300,"startOfParagraph":false},{"content":"Before I write the HTML for that, I'm going to really quickly","startTime":2894660,"duration":5080,"startOfParagraph":false},{"content":"show you what the structure of this errors variable looks like,","startTime":2899740,"duration":1980,"startOfParagraph":false},{"content":"and this is a good practice in general.","startTime":2901720,"duration":1360,"startOfParagraph":false},{"content":"A lot of times you get something back from some method,","startTime":2903080,"duration":1990,"startOfParagraph":false},{"content":"some function in the web framework,","startTime":2905070,"duration":2180,"startOfParagraph":false},{"content":"and you don't know what the variable looks like,","startTime":2907250,"duration":2160,"startOfParagraph":false},{"content":"so you don't know how to work with it.","startTime":2909410,"duration":1800,"startOfParagraph":false},{"content":"I'm going to use a print r method to basically print it out.","startTime":2911210,"duration":6580,"startOfParagraph":false},{"content":"And as you can see, it tells me it's an associate array,","startTime":2917790,"duration":3310,"startOfParagraph":true},{"content":"and you have a key, author, points to this string,","startTime":2921100,"duration":3780,"startOfParagraph":false},{"content":"author must not be empty, and another key, body,","startTime":2924880,"duration":2170,"startOfParagraph":false},{"content":"points to another string, body must not be empty.","startTime":2927050,"duration":2630,"startOfParagraph":false},{"content":"I'm like, okay, cool.","startTime":2929680,"duration":2450,"startOfParagraph":false},{"content":"Then I can iterate through the array and print out every single message.","startTime":2932130,"duration":4100,"startOfParagraph":false},{"content":"It's basically like an associative array with a bunch of messages.","startTime":2936230,"duration":5920,"startOfParagraph":false},{"content":"What I'm going to do is \"if errors,\"","startTime":2942150,"duration":11350,"startOfParagraph":false},{"content":"and I'm going to create an unordered list,","startTime":2953500,"duration":3640,"startOfParagraph":false},{"content":"and I'm going to iterate through all the errors.","startTime":2957140,"duration":3720,"startOfParagraph":false},{"content":"And this, and now I'm going to try submitting this again,","startTime":2973730,"duration":4980,"startOfParagraph":false},{"content":"and let's see what we get.","startTime":2978710,"duration":2490,"startOfParagraph":false},{"content":"Now we get this nice list of errors,","startTime":2981200,"duration":4170,"startOfParagraph":false},{"content":"and this is still pretty ugly, but this obviously can be formatted to look nice,","startTime":2985370,"duration":3260,"startOfParagraph":false},{"content":"but the basic idea is just in a few lines of code,","startTime":2988630,"duration":2990,"startOfParagraph":false},{"content":"we were able to validate our model,","startTime":2991620,"duration":2100,"startOfParagraph":false},{"content":"make sure that certain fields weren't empty,","startTime":2993720,"duration":2790,"startOfParagraph":false},{"content":"and if something went wrong, then return some sort of error message","startTime":2996510,"duration":3230,"startOfParagraph":false},{"content":"I could then present back to the user.","startTime":2999740,"duration":2020,"startOfParagraph":false},{"content":"You can also customize your validation","startTime":3001760,"duration":2150,"startOfParagraph":false},{"content":"so that you can actually have an error message","startTime":3003910,"duration":3680,"startOfParagraph":false},{"content":"that is more specific to your application or something like that.","startTime":3007590,"duration":2030,"startOfParagraph":false},{"content":"All that is generally customizable.","startTime":3009620,"duration":4980,"startOfParagraph":false},{"content":"Unfortunately, we're running out of time,","startTime":3014600,"duration":2550,"startOfParagraph":true},{"content":"so I'm going to have to cut off the live coding session here.","startTime":3017150,"duration":2890,"startOfParagraph":false},{"content":"There are a bunch of other features that I want to demonstrate for you","startTime":3020040,"duration":2940,"startOfParagraph":false},{"content":"in this example.","startTime":3022980,"duration":2670,"startOfParagraph":false},{"content":"For example, you can add templates to your site,","startTime":3025650,"duration":1970,"startOfParagraph":false},{"content":"so maybe there is some sort of HTML code that you want to apply","startTime":3027620,"duration":3490,"startOfParagraph":false},{"content":"to every single page in your site, and instead of pasting that","startTime":3031110,"duration":4080,"startOfParagraph":false},{"content":"in every single view file you have, which obviously would be a bad practice,","startTime":3035190,"duration":5440,"startOfParagraph":false},{"content":"you can basically define these templates,","startTime":3040630,"duration":2390,"startOfParagraph":false},{"content":"and then in your controller say, \"Okay, I'm using this template.\"","startTime":3043020,"duration":3640,"startOfParagraph":false},{"content":"\"Have all my views use this template.\"","startTime":3046660,"duration":3470,"startOfParagraph":false},{"content":"And the one last thing I want to demonstrate to you as well","startTime":3050130,"duration":2340,"startOfParagraph":false},{"content":"that we don't have time for is cross-site scripting,","startTime":3052470,"duration":5330,"startOfParagraph":false},{"content":"and basically I think you guys have probably seen in CS50--","startTime":3057800,"duration":3630,"startOfParagraph":false},{"content":"I think David Malan probably talked about how you can usually inject","startTime":3061430,"duration":2340,"startOfParagraph":false},{"content":"JavaScript code into--have you talked about this?","startTime":3063770,"duration":4270,"startOfParagraph":false},{"content":"Maybe? Maybe not?","startTime":3068040,"duration":2180,"startOfParagraph":false},{"content":"But a lot of times you can inject malicious JavaScript code","startTime":3070220,"duration":2450,"startOfParagraph":true},{"content":"into someone's database, and if they don't escape that properly,","startTime":3072670,"duration":2960,"startOfParagraph":false},{"content":"then when they present that data back to the user,","startTime":3075630,"duration":2650,"startOfParagraph":false},{"content":"then it may run some sort of random JavaScript code you don't want to happen,","startTime":3078280,"duration":3030,"startOfParagraph":false},{"content":"and I was going to demonstrate how you'd do that within Kohana.","startTime":3081310,"duration":1740,"startOfParagraph":false},{"content":"It's actually really, really easy.","startTime":3083050,"duration":2330,"startOfParagraph":false},{"content":"I could do it right now in 2 seconds literally.","startTime":3085380,"duration":6540,"startOfParagraph":false},{"content":"All you have to do is basically wrap these things","startTime":3091920,"duration":2640,"startOfParagraph":false},{"content":"in this thing called HTML entities.","startTime":3094560,"duration":12360,"startOfParagraph":false},{"content":"And that will automatically escape all the characters properly","startTime":3106920,"duration":4260,"startOfParagraph":false},{"content":"and make sure you don't get this problem.","startTime":3111180,"duration":3550,"startOfParagraph":false},{"content":"[Student] You spelled the first test incorrectly.","startTime":3114730,"duration":2490,"startOfParagraph":false},{"content":"[Brandon Liu] Oh, oops.","startTime":3117220,"duration":3810,"startOfParagraph":false},{"content":"Okay, that's all I had to share with you for today.","startTime":3121030,"duration":5390,"startOfParagraph":true},{"content":"These slides are going to be posted, but these are generally","startTime":3126420,"duration":3510,"startOfParagraph":false},{"content":"the only resources you should really need to get started with Kohana.","startTime":3129930,"duration":3970,"startOfParagraph":false},{"content":"You can go to the website. They have a user guide, and they also have an API explorer.","startTime":3133900,"duration":2870,"startOfParagraph":false},{"content":"We can explore all the different functions and helper functions they have for you.","startTime":3136770,"duration":3860,"startOfParagraph":false},{"content":"They generally have enough information on the website","startTime":3140630,"duration":1440,"startOfParagraph":false},{"content":"that you can use to get started and get going with Kohana.","startTime":3142070,"duration":3240,"startOfParagraph":false},{"content":"There aren't that many tutorials, I think, for Kohana, outside of","startTime":3145310,"duration":5530,"startOfParagraph":false},{"content":"what they have on the website here, so this is probably your best bet.","startTime":3150840,"duration":3040,"startOfParagraph":false},{"content":"But if you want to go with the web framework","startTime":3153880,"duration":1720,"startOfParagraph":false},{"content":"and you don't want to have to pick up a new language,","startTime":3155600,"duration":1520,"startOfParagraph":false},{"content":"and you want something that is relatively lightweight and has an easy learning curve,","startTime":3157120,"duration":2660,"startOfParagraph":false},{"content":"I would definitely suggest Kohana.","startTime":3159780,"duration":1790,"startOfParagraph":false},{"content":"That's probably the best offering for that.","startTime":3161570,"duration":2470,"startOfParagraph":false},{"content":"The funny thing, though, is if we were using Ruby on Rails,","startTime":3164040,"duration":2790,"startOfParagraph":false},{"content":"we could have replicated what we just did","startTime":3166830,"duration":1720,"startOfParagraph":false},{"content":"and probably more in under 3 minutes.","startTime":3168550,"duration":2880,"startOfParagraph":false},{"content":"No joke, but learning Ruby on Rails takes a lot longer","startTime":3171430,"duration":3280,"startOfParagraph":false},{"content":"than it would take to learn Kohana.","startTime":3174710,"duration":2070,"startOfParagraph":false},{"content":"It's basically your choice on what you want to choose to learn,","startTime":3176780,"duration":2060,"startOfParagraph":false},{"content":"but if you want to get up and running quickly,","startTime":3178840,"duration":2420,"startOfParagraph":false},{"content":"Kohana is definitely a very good choice.","startTime":3181260,"duration":2610,"startOfParagraph":false},{"content":"Any last questions before we end? Yes.","startTime":3183870,"duration":2860,"startOfParagraph":true},{"content":"[Student] How would we integrate that","startTime":3186730,"duration":1290,"startOfParagraph":false},{"content":"in a CSS framework like you were using when you were instructing?","startTime":3188020,"duration":5100,"startOfParagraph":false},{"content":"The question is how would we integrate that with a CSS framework?","startTime":3193120,"duration":3580,"startOfParagraph":false},{"content":"What we would probably do is we would probably include","startTime":3196700,"duration":2040,"startOfParagraph":false},{"content":"a new folder where we would dump all our CSS files,","startTime":3198740,"duration":2350,"startOfParagraph":false},{"content":"and then we'd also add a new template.","startTime":3201090,"duration":1920,"startOfParagraph":false},{"content":"In the template we'd include those CSS files","startTime":3203010,"duration":3080,"startOfParagraph":false},{"content":"to make sure they're referenced on every single page,","startTime":3206090,"duration":2320,"startOfParagraph":false},{"content":"and then when you actually are writing HTML,","startTime":3208410,"duration":3810,"startOfParagraph":false},{"content":"you just add appropriate classes and whatnot, ","startTime":3212220,"duration":1790,"startOfParagraph":false},{"content":"and for example, when you're using something like the form","startTime":3214010,"duration":2090,"startOfParagraph":false},{"content":"helper function, you can add more parameters afterwards","startTime":3216100,"duration":4610,"startOfParagraph":false},{"content":"to specify what classes you want to be attached to various things","startTime":3220710,"duration":2120,"startOfParagraph":false},{"content":"so they could style it properly, and that's basically how you would go.","startTime":3222830,"duration":4990,"startOfParagraph":false},{"content":"Any other questions?","startTime":3227820,"duration":2280,"startOfParagraph":true},{"content":"Awesome. ","startTime":3230100,"duration":1990,"startOfParagraph":false},{"content":"Thank you for your time, and thank you for coming.","startTime":3232090,"duration":6450,"startOfParagraph":false},{"content":"I wasn't going to add very much else,","startTime":3238540,"duration":6630,"startOfParagraph":false},{"content":"but 1 really quick thing is we don't have a link","startTime":3245170,"duration":3390,"startOfParagraph":false},{"content":"to the form.","startTime":3248560,"duration":4030,"startOfParagraph":false},{"content":"Really dumb.","startTime":3252590,"duration":1720,"startOfParagraph":false},{"content":"Let's add a--actually in the view, blog, index,","startTime":3254310,"duration":5800,"startOfParagraph":false},{"content":"let's really quickly add a link that goes to the new page,","startTime":3260110,"duration":3780,"startOfParagraph":false},{"content":"the page where we can insert a new post.","startTime":3263890,"duration":2880,"startOfParagraph":false},{"content":"We're going to do this.","startTime":3266770,"duration":3180,"startOfParagraph":false},{"content":"What's nice is there is this whole group of HTML helper functions","startTime":3269950,"duration":4070,"startOfParagraph":false},{"content":"which do different things for you, so you already saw the entities","startTime":3274020,"duration":3070,"startOfParagraph":false},{"content":"function here, but they also have a function called \"anchor,\"","startTime":3277090,"duration":4890,"startOfParagraph":false},{"content":"which you can type in blog/new","startTime":3281980,"duration":3420,"startOfParagraph":false},{"content":"and say, \"Post a new blog.\"","startTime":3285400,"duration":4150,"startOfParagraph":false},{"content":"And it would create that link for you,","startTime":3289550,"duration":2300,"startOfParagraph":false},{"content":"and this seems really trivial to do, but this is nice,","startTime":3291850,"duration":2270,"startOfParagraph":false},{"content":"because suppose that you are moving your website from 1 domain to another.","startTime":3294120,"duration":4600,"startOfParagraph":false},{"content":"And if you just wrote out the URLs yourself,","startTime":3298720,"duration":2670,"startOfParagraph":false},{"content":"then you would have to change all the URLs.","startTime":3301390,"duration":2960,"startOfParagraph":false},{"content":"Or maybe you moved it from 1 subfolder to another subfolder.","startTime":3304350,"duration":2500,"startOfParagraph":false},{"content":"You would have to change all those URLs yourself,","startTime":3306850,"duration":1940,"startOfParagraph":false},{"content":"and that's no fun.","startTime":3308790,"duration":3390,"startOfParagraph":false},{"content":"You can use this anchor right here,","startTime":3312180,"duration":2330,"startOfParagraph":false},{"content":"and you can change the domain or the subfolder prefix","startTime":3314510,"duration":4440,"startOfParagraph":false},{"content":"in the configuration file once,","startTime":3318950,"duration":1690,"startOfParagraph":false},{"content":"and then it will apply that everywhere, and this is, again,","startTime":3320640,"duration":2340,"startOfParagraph":false},{"content":"a great example of do not repeat yourself, DRYing your code out.","startTime":3322980,"duration":3950,"startOfParagraph":false},{"content":"Wherever you're repeating yourself, try and extract in some sort of configuration file","startTime":3326930,"duration":3440,"startOfParagraph":false},{"content":"or to a different function and have it handle that for you.","startTime":3330370,"duration":3790,"startOfParagraph":false},{"content":"And the very last thing that I wanted to show you was","startTime":3334160,"duration":8770,"startOfParagraph":true},{"content":"suppose we're back at this post, and I had composed some really long essay,","startTime":3342930,"duration":7320,"startOfParagraph":false},{"content":"but I forgot to include my author.","startTime":3350250,"duration":2420,"startOfParagraph":false},{"content":"Now when I click \"Submit Post,\"","startTime":3352670,"duration":2540,"startOfParagraph":false},{"content":"I just lost everything.","startTime":3355210,"duration":2060,"startOfParagraph":false},{"content":"No! Really sad.","startTime":3357270,"duration":2730,"startOfParagraph":false},{"content":"So how do you deal with that?","startTime":3360000,"duration":3870,"startOfParagraph":false},{"content":"This is what we do.","startTime":3363870,"duration":2200,"startOfParagraph":false},{"content":"What we do is here for these input and text area functions,","startTime":3366070,"duration":3270,"startOfParagraph":false},{"content":"if we include a second parameter, then the value of that second parameter","startTime":3369340,"duration":3360,"startOfParagraph":false},{"content":"is going to be what the field is going to be initially populated with.","startTime":3372700,"duration":3920,"startOfParagraph":false},{"content":"What we could do is in our blog controller,","startTime":3376620,"duration":6950,"startOfParagraph":false},{"content":"we could bind another variable.","startTime":3383570,"duration":1790,"startOfParagraph":false},{"content":"Call it \"values\" maybe.","startTime":3385360,"duration":1690,"startOfParagraph":false},{"content":"And pass in the post array, literally.","startTime":3387050,"duration":3570,"startOfParagraph":false},{"content":"That means that if the validation failed,","startTime":3390620,"duration":2000,"startOfParagraph":false},{"content":"pass to me the post array that I submitted from the last request,","startTime":3392620,"duration":3950,"startOfParagraph":false},{"content":"and that way I can use the values from my last submission","startTime":3396570,"duration":1850,"startOfParagraph":false},{"content":"to repopulate the fields.","startTime":3398420,"duration":6120,"startOfParagraph":false},{"content":"Now I can do something like values author","startTime":3404540,"duration":5060,"startOfParagraph":false},{"content":"and values body, and that way now if I do some random stuff","startTime":3409600,"duration":5580,"startOfParagraph":false},{"content":"and click \"Submit Post,\" then it stays there.","startTime":3415180,"duration":6310,"startOfParagraph":false},{"content":"But we're going to run into another problem.","startTime":3421490,"duration":2340,"startOfParagraph":false},{"content":"That works, but if I go to the page the very first time,","startTime":3423830,"duration":3840,"startOfParagraph":false},{"content":"it's going to crash, and that's because the very first time","startTime":3427670,"duration":2050,"startOfParagraph":false},{"content":"we go to the page, this post variable has not been defined yet.","startTime":3429720,"duration":4010,"startOfParagraph":false},{"content":"It's null. It doesn't exist.","startTime":3433730,"duration":4440,"startOfParagraph":false},{"content":"And what we want to say is if this key exists,","startTime":3438170,"duration":3460,"startOfParagraph":true},{"content":"then return the value of this array,","startTime":3441630,"duration":6120,"startOfParagraph":false},{"content":"but if the key doesn't exist, then return a blank string.","startTime":3447750,"duration":2700,"startOfParagraph":false},{"content":"That's the functionality we want here.","startTime":3450450,"duration":1700,"startOfParagraph":false},{"content":"We want to check if the key exists before trying to access the array,","startTime":3452150,"duration":2540,"startOfParagraph":false},{"content":"and fortunately enough,","startTime":3454690,"duration":1890,"startOfParagraph":false},{"content":"Kohana also gives us a helper function for that.","startTime":3456580,"duration":1990,"startOfParagraph":false},{"content":"They have this whole suite of functions","startTime":3458570,"duration":2470,"startOfParagraph":false},{"content":"under the name ARR, short for array,","startTime":3461040,"duration":2620,"startOfParagraph":false},{"content":"and they have 1 function called \"get,\"","startTime":3463660,"duration":2140,"startOfParagraph":false},{"content":"and you can pass in the array,","startTime":3465800,"duration":2890,"startOfParagraph":false},{"content":"and you can pass in the name of the key.","startTime":3468690,"duration":2050,"startOfParagraph":false},{"content":"Then basically what it will do is it will try to get that key,","startTime":3470740,"duration":3590,"startOfParagraph":false},{"content":"but if that key doesn't exist in the array,","startTime":3474330,"duration":2140,"startOfParagraph":false},{"content":"then it will return blank, or we can also specify a default, I believe,","startTime":3476470,"duration":4430,"startOfParagraph":false},{"content":"which is nice.","startTime":3480900,"duration":2600,"startOfParagraph":false},{"content":"Now if we do the same thing again,","startTime":3489740,"duration":3410,"startOfParagraph":false},{"content":"then you see now it works the first time around,","startTime":3493150,"duration":2820,"startOfParagraph":false},{"content":"and again, if we type in some random stuff","startTime":3495970,"duration":2110,"startOfParagraph":false},{"content":"and try and submit, then it stays there.","startTime":3498080,"duration":5130,"startOfParagraph":false},{"content":"And I guess I can also show you how to add a template really quickly.","startTime":3503210,"duration":8430,"startOfParagraph":true},{"content":"What we can do first is we can add a new view called \"template.php\"","startTime":3511640,"duration":4500,"startOfParagraph":false},{"content":"within the Views folder,","startTime":3516140,"duration":2750,"startOfParagraph":false},{"content":"and what I'm going to do is I'm going to print out something called \"content,\"","startTime":3518890,"duration":5840,"startOfParagraph":false},{"content":"which is going to be my main content.","startTime":3524730,"duration":4400,"startOfParagraph":false},{"content":"And maybe at the very bottom I'm going to add, say,","startTime":3529130,"duration":2250,"startOfParagraph":false},{"content":"copyright.","startTime":3531380,"duration":1960,"startOfParagraph":false},{"content":"[inaudible student question]","startTime":3533340,"duration":2810,"startOfParagraph":false},{"content":"[Brandon Liu] Maybe this is a super basic template I want to use.","startTime":3536150,"duration":1900,"startOfParagraph":false},{"content":"I want to have a folder with my copyright on every single page,","startTime":3538050,"duration":4790,"startOfParagraph":false},{"content":"and now what I'm going to do within my controller","startTime":3542840,"duration":2720,"startOfParagraph":false},{"content":"is now instead of saying, \"extends Controller\"","startTime":3545560,"duration":2180,"startOfParagraph":false},{"content":"I'm going to say, \"extends Controller_Template,\"","startTime":3547740,"duration":4130,"startOfParagraph":false},{"content":"and now instead of saying, \"response body is equal to this view,\"","startTime":3551870,"duration":4020,"startOfParagraph":false},{"content":"I'm going to say, \"this template content is --\"","startTime":3555890,"duration":8220,"startOfParagraph":false},{"content":"and I think--do I put an equal sign?","startTime":3564110,"duration":3580,"startOfParagraph":false},{"content":"I forget. Yeah, I thought so.","startTime":3567690,"duration":5020,"startOfParagraph":false},{"content":"And now I set that content variable to equal the view.","startTime":3572710,"duration":5000,"startOfParagraph":false},{"content":"I can do the same here.","startTime":3577710,"duration":3250,"startOfParagraph":false},{"content":"And now if I refresh, you can see now this copyright is added there,","startTime":3589620,"duration":7550,"startOfParagraph":false},{"content":"and just make some random post,","startTime":3597170,"duration":3180,"startOfParagraph":false},{"content":"and then, again, you should see that the copyright is at the very bottom of the page.","startTime":3600350,"duration":6410,"startOfParagraph":false},{"content":"Great. That's all I wanted to show you guys.","startTime":3606760,"duration":3970,"startOfParagraph":true},{"content":"[Applause]","startTime":3610730,"duration":4240,"startOfParagraph":false},{"content":"Any questions?","startTime":3614970,"duration":3980,"startOfParagraph":false},{"content":"[CS50.TV]","startTime":3618950,"duration":2050,"startOfParagraph":false}]}