در حال بارگذاری ...

Project Roots Overview0:00

All right, welcome back. I will warn you up front, we're still focused on project organization in this video, and I'm going to make a lot of small adjustments and tweaks. So I tell you this because if you typically work along in your own code editor for each video, maybe for this lesson alone, don't do that. Just watch it. Come along for the ride, and then at the end of the video, you'll have a link to the source code, and at that point, you can update your own project. All right, let's get started. So let's begin by discussing the project roots.

All right, let's get started. So let's begin by discussing the project roots. So why don't we start by opening any file here, and at the very top, I'm going to var_dump and write, hello there. Okay, so have you ever considered what would happen if the User tries to access one of these files from the browser? All right, let's give it a shot. So we load the homepage, and yeah, we see hello there, but we'd expect that. We are requiring the router, after all. But yeah, now let's access router.php directly, router.php, and it works.

Direct File Access Risk0:54

We are requiring the router, after all. But yeah, now let's access router.php directly, router.php, and it works. And just to make this crystal clear, let's die afterward to prove that we are accessing and triggering and executing that file directly, and as it turns out, this is a big security concern that we didn't anticipate. Okay, so yeah, if you think about it, right now, our document root, so to speak, is the demo folder. It's the root of our folder. And normally, all requests are going through index.php. However, the way the built-in server works is, well, if you have another file that exists

And normally, all requests are going through index.php. However, the way the built-in server works is, well, if you have another file that exists and that can be matched, then it will load that file instead. So yeah, let's just try a different one, like config.php, and we'll say, echo, hello there. Okay, now let's try to load that from the browser. config.php, and we get hello there. So again, a big, big security concern, and we can't allow this. So let's do this. First, let me undo our examples here, and I'm going to change the document root. And this document root is typically your public folder.

Move Entry to Public1:57

First, let me undo our examples here, and I'm going to change the document root. And this document root is typically your public folder. This is where your entry point will be, so index.php will go there, and it's also where you'd store, for example, style sheets or images or JavaScript files. So let's do that now. Create a directory called public, and I will move index.php within there. But just creating the folder isn't enough. When we initialize or boot up our server, we need to tell it what the document root should be. So real quick, if I run php -h, let's look for -t, yeah, so when we boot up the server,

should be. So real quick, if I run php -h, let's look for -t, yeah, so when we boot up the server, I can pass the -t option to specify what my document root for the built-in server should be. So let's do it again. Create a server at localhost:8888, -t, and the document root will now be not the root of the project, but instead the public folder. Okay, so now, yeah, let's refresh this page, and notice we get a not found, because it doesn't know anything about this config file. All it knows about is this public folder, because this is now the document root.

Base Path Helper3:03

doesn't know anything about this config file. All it knows about is this public folder, because this is now the document root. Okay, so let's switch back to the browser and tackle the next problem. If we load the root URL, now it's trying to require functions, but it can't find it, and again, that's because we moved it into a new directory. Okay, so rather than constantly updating our require paths, I think this is a good opportunity to choose a more seamless implementation, and I'll show you how. Now, the first thing I might do is declare a constant for the project, and I will call this basePath, which will point to a path, an absolute path, to the root of your project, and again, people often call this basePath.

this basePath, which will point to a path, an absolute path, to the root of your project, and again, people often call this basePath. So given that I'm in index.php, well, the root of this project would be the current directory, as we learned about in the last episode, and then let's go up a level, like that. So this should take me to the demo folder, and we can confirm that by saying var_dump(basePath). Okay, come back, give it a refresh, and yet it's still failing, but notice now, this is our path to the demo folder. Okay, so yeah, the next step, and we're not going to keep this, but the next step would

our path to the demo folder. Okay, so yeah, the next step, and we're not going to keep this, but the next step would be to update all of these requires, so you might do something like this, require basePath and then functions. So now if I come back, you'll see that it still fails, but the issue will be related to a different file that it's trying to require. Yeah, now it's trying to load database. So again, you could do this, basePath, but again, I don't know, I just don't love it. Come back, give it a refresh, and yeah, that worked, but now we've moved on to the next issue further down the rabbit hole.

Come back, give it a refresh, and yeah, that worked, but now we've moved on to the next issue further down the rabbit hole. Yeah, so this is what I mean when I say maybe we should consider a different approach for requiring files. So here's what I'll do. I'm going to go into functions.php, and at the bottom, I will add two helper functions for declaring a path that is relative to the root of the project. So we're going to call this basePath, and we'll accept the path. And then what I'll do, and let me give it a quick reformat here, then what we'll do is return basePath, our constant, and then we will concatenate the path that is provided.

And then what I'll do, and let me give it a quick reformat here, then what we'll do is return basePath, our constant, and then we will concatenate the path that is provided when we call the function. So again, it's not too complex, but it'll just make it a little easier to interact with. And now whenever we need to declare a path relative to the basePath, I don't have to remember what the name of the constant is. Okay, so let's come back, and now for these three, what I could do is select them and use our new basePath helper function. And yeah, of course, I can't use our new helper function for this require, because it's for helperfunctions.php, and that helper function doesn't exist yet.

And yeah, of course, I can't use our new helper function for this require, because it's for helperfunctions.php, and that helper function doesn't exist yet. Okay, so now if I come back and refresh, yeah, we still get the same issue, but we've created a nice little wrapper that we can use throughout the entire codebase. Okay, so let's do that now. Let's solve this warning by going into controllers/index. And here you can see we're trying to load a view, but this time I'm going to swap it out with basePath, and then our view. Okay, so switch back, give it a refresh, and there we go. We're up and running.

Create View Helper6:29

Okay, so switch back, give it a refresh, and there we go. We're up and running. Okay, but I think we can take this even further. As you can imagine, loading a view is something you will do constantly in a project. So with that in mind, maybe it would make sense to add a helper function like view, and then I just provide the name of the view that I want to load. Okay, that would be pretty cool. Why don't we allow for it? index.php, add a new one here, view, path, and then this would load, think about it, we can reuse basePath, so call basePath, and then we will start at the views directory,

index.php, add a new one here, view, path, and then this would load, think about it, we can reuse basePath, so call basePath, and then we will start at the views directory, and then we will concatenate the path to the user's provided view. So yeah, in this case, we have index.view.php, so this would load our basePath slash views slash index.view.php, and I think that's what we want. All right, so with any luck, come back and refresh. Everything still works, but now this is just a more friendly API, so to speak. It's easier for me to use and easier for me to grasp that we are requiring a view. But even better, and you might like this, what if we move the require statement into the view function as well?

Pass Data to Views7:43

But even better, and you might like this, what if we move the require statement into the view function as well? That might be cool. Okay, so we give that a try, but I bet we run into a little snag here. Come back, give it a refresh, and yeah, we have a warning, undefined variable heading. Okay, so if you want, pause the video and see if you can figure out what happened here. Okay, here's the issue. Notice that we are loading a view, and that view is looking for a heading variable. However, the heading variable was declared in the controller, and now we are requiring the view from an entirely different scope,

However, the heading variable was declared in the controller, and now we are requiring the view from an entirely different scope, and that's why it can't find it. So yeah, it sounds like if we want to take this approach, and I like it a lot, we probably need a way to pass all of our view data along with this function call. So maybe we could pass an array as the second argument, and yeah, we could do something like this, heading is heading, or in this case, I could just inline it. So I could swap this out with home, like so. Okay, but now I need to go into the view function and accept the attributes,

So I could swap this out with home, like so. Okay, but now I need to go into the view function and accept the attributes, which will default to an empty array. Okay, but of course, it's still not going to work because we aren't yet doing anything with that attributes array. Okay, well, as it turns out, php has a function called extract. So first, come along for the ride. I will call extract, switch back, and that does solve the problem. So let's see what extract does. I'm going to click through here with command click, and yeah, we can see it.

So let's see what extract does. I'm going to click through here with command click, and yeah, we can see it imports variables into the current symbol table from an array. Okay, that makes it sound super complicated. Just think of it like this. It accepts an array, and it turns that array into a set of variables, where the name of the variable is the key, and the value of the variable is the value associated with the key. So for example, if I also did $foo is $bar, well then, once I extract it, I would then have a, I'm sorry, I would then have a $foo variable.

So for example, if I also did foo is bar, well then, once I extract it, I would then have a, I'm sorry, I would then have a foo variable. And in fact, let's just try it out. die and dump foo, and this should be bar, and you see how that works. Super, super handy stuff here. Okay, so I think that is the way to go. All right, now let's update our other controllers. So I will go to about. I'll do the same thing here, but load about.view, and then update this. All right, then we will go into contact, and you know what?

I'll do the same thing here, but load about.view, and then update this. All right, then we will go into contact, and you know what? I'm going to speed all of this up. So I'm going to hit fast forward right now. Okay, and that's enough. Yeah, it's the exact same process there. But now if we go into notes, this will be slightly different. So we'll start here, and I'll paste this in. We're going to load notes/index.view.php. The heading is my notes, so I can get rid of this.

We're going to load notes/index.view.php. The heading is myNotes, so I can get rid of this. And then really, I don't need to access the db variable and the config variable from my view. And as it turns out, before this refactor, you could have done that, which might have led to potential issues. But now we're avoiding that entirely. So we'll just say notes is notes. And now, these two variables, heading and notes, are the only ones that will be available when we load our view.

And now, these two variables, heading and notes, are the only ones that will be available when we load our view. Okay, so now I will copy this and update these as well. This will be notes/create. What is our heading? It's create note. And what do we need here? Okay, well, maybe an errors variable, and I think that's it. All right, and then we'll go into show and do the exact same thing. So paste this one in, and we have notes/show.

All right, and then we'll go into show and do the exact same thing. So paste this one in, and we have notes/show. This is notes, and then I'm going to pass through a note variable that corresponds to this. Okay, so I can get rid of that, and I think that's right. So yeah, a lot of changes here. Let's see if I made any mistakes. Come back and refresh, home, about, contact, notes. Now we have a new issue. So it looks like in notes/index, yeah, once again,

Now we have a new issue. So it looks like in notes/index, yeah, once again, we are trying to require config, but now we've changed our document route. So this is another case where I can require base_path and then config.php. Come back, refresh, and now when I go into my view, yeah, this is what I meant when I said just come along for the ride, because when you make these sweeping project changes, you have to update lots of file paths. Yeah, so now, let's see, we're trying to load head.php, and we have to update all of these to now reference our base_path.

Yeah, so now, let's see, we're trying to load head.php, and we have to update all of these to now reference our base path. So I will use multiple cursors and change this to require base path, and that'll be a fine and quick way to deal with this. So come back and give it a refresh, and that solves the problem. Okay, so once again, I will hit Fast Forward and update all of the other views where we've required the exact same partials. Okay, All right, and that's done. Come back, refresh, once again, home, about, notes, contact. Let's view a note, and same thing.

Okay, good, and then to create a note, require validator. Okay, good, this brings us to the next thing I wanna review. All right, let's open up our CreateController. And yeah, here, I'm trying to require my validator. And again, I could do this base_path, come back, give it a refresh. Okay, and then it looks like undefined variable errors. Yeah, that's because we only declare the errors variable if the form was submitted. Why don't we do that no matter what at the top? So no matter what, there will be an errors variable available to the view.

Why don't we do that no matter what at the top? So no matter what, there will be an errors variable available to the view. Come back, give it a refresh, and that solves it. Finally, let's create a new Note. Mm-hm, come on back. Yeah, everything is up and running. So lots of little changes, like I said, but I think it's for the better. But we're still not done. I want you to notice for any of these controllers, how about this one here, where we display all notes?

Autoload Classes14:34

I want you to notice for any of these controllers, how about this one here, where we display all notes? It's just assumed that we have access to a database class. And we know that's the case because we declare as much with an index.php. But I don't like the idea where every time I need access to a new class, I have to require it with an index.php. Not a good approach. Instead, it would be cool if there was a way to lazily and automatically load and require these classes when I instantiate them or when I need them. Okay, so let's try this out.

and require these classes when I instantiate them or when I need them. Okay, so let's try this out. Why don't we get rid of the database and response requires? Because now we're going to change it up. So now when we load index.php, we declare a constant, we pull in our helper functions, and then we require the router that parses the requests and figures out what controller needs to be loaded. Okay, so that means if I come back to Firefox and I give it a refresh, yeah, and now, as expected, our controller no longer knows what database is. Okay, so let's switch back.

yeah, and now, as expected, our controller no longer knows what database is. Okay, so let's switch back. And let's see, right here, I'm going to call this scary php function called spl_autoload_register. Don't worry, it's not too bad. This is going to accept a class. And just to get us rolling, I will die and dump the class so you can see when this function is automatically triggered by php. All right, switch back to Firefox, give it a refresh, and aha, we have called that function or php called that function automatically.

All right, switch back to Firefox, give it a refresh, and aha, we have called that function or php called that function automatically. And here's why. If I go into the controller, temporarily, just to show you, I will comment all of that out. And yeah, this line right here is what's triggering that call. So if I come back and refresh, yeah, you still see it there. Okay, so the issue is php doesn't know what that database class is, so it's trying to track it down. And we have declared our implementation, so to speak,

So we can use our helper function, base_path. Okay, but actually, you know what? Before I do that, let's pass this to var_dump just so you can see exactly what we are trying to require. All right, back to Firefox, give it a refresh. And yeah, right up here, this is what we are trying to load. All right, let's see if it works. Bring it back, come back, give it a refresh. And there we go, that fixed it. Okay, so now these other warnings are just because we commented these out.

And there we go, that fixed it. Okay, so now these other warnings are just because we commented these out. So let's bring it back, like so, come back, give it a refresh, and there we go. Perfect, so that's kind of a handy function, right? It lets us declare manually how we wanna go about importing classes that have not already been explicitly or manually required. Okay, so we're about ready to finish up for this lesson. But the last little thing, and then by the way, in the next video, we will dig into namespaces, which should be fun. So I want you to notice how I have a bunch of classes here,

Extract Core Directory17:41

we will dig into namespaces, which should be fun. So I want you to notice how I have a bunch of classes here, like database, and response, and validator, that aren't uniquely related to the application we're building. Or in other words, if I was building a to-do app, I would have classes like ToDoItem, maybe TaskList, maybe Project, maybe Account, those are all unique to my application. But validation, or a response, or interacting with a database, that's very generic, that's very infrastructure-related. So why don't we separate those a bit?

that's very generic, that's very infrastructure-related. So why don't we separate those a bit? And we can call this directory anything we want, but I'm just gonna call it for now, like core, just kind of deep-down core stuff. And then I will grab those, and how about, hmm, validator, maybe the router, maybe response, maybe database. I could even move functions there if I want. Actually, maybe I will as well. I'll grab those and move them into our new core directory. Okay, but now, of course, if I come back and refresh,

I'll grab those and move them into our new core directory. Okay, but now, of course, if I come back and refresh, the paths have changed, so now it can't find the file. Okay, that's an easy fix, though. Let's go into our autoloader, and yeah, we will need to tweak our require statement. And actually, on this note, a quick warning, in the next episode, when we get into namespacing, we'll have to rewrite this again. But that's okay, one step at a time.

we'll have to rewrite this again. But that's okay, one step at a time. So yeah, look in the core directory and then find the class. Or we can inline all of this. So I'm using phpStorm, so I can option return, and I can choose convert concatenation to string interpolation. And I think that looks a little better. Okay, come back to Firefox, give it a refresh, and there we go. Okay, so I'm gonna leave it at that. I think we made some great strides here.

Okay, so I'm gonna leave it at that. I think we made some great strides here. So in the next video, as mentioned, we will dig into namespaces.

Autoloading ClassesDocument Rootextract()

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟