تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Goal: Discoverable Tasks0:00

Before moving on to building the final bit of automation in this series, I want to make sure that this task runner is extendable. Right now, we just have these three tasks, and they're statically registered in this method for our run command. Something I've always wanted to do with Shift is to open up the automation to the community. And instead of having developers submit PRs to this Shift console, they could build standalone automated tasks and still run them through this Shift tool. So I want to build a command that can automatically discover other tasks, and that makes me think of Laravel's package:discover command. You probably see this in action anytime you run a composer command.

Create Discover Command0:46

of Laravel's package discover command. You probably see this in action anytime you run a composer command. Laravel automatically scans all of the installed packages within your project to see if they have any additional configuration which Laravel should automatically load. And I'm going to take this exact same approach to see if there are any other automated tasks within the project that are available to run. To do that, I'm going to make an additional discover command. So let's jump back to the command line and do php artisan make:command DiscoverCommand. All right, there we have it. Just as before, let's trim this down.

All right, there we have it. Just as before, let's trim this down. We'll make this discover. There aren't any arguments or options for this command. And for the description, we'll say load any additional automated tasks within the project. All right, let's kill that extra comment. This is not run on a schedule, so let's kill that as well. And in the handle command, we would want to make this look much like Laravel's discover command. And we can actually find that within Laravel 0 in the package manifest file. And if we scroll through this file, we'll see that underneath getManifest, we'll see that if the

Build Task Manifest1:58

And we can actually find that within Laravel 0 in the package manifest file. And if we scroll through this file, we'll see that underneath getManifest, we'll see that if the manifest is not loaded already and the cache file for that manifest is not available, then we'll build one. And this gets a list of all of the installed packages, which is available in this JSON file from Composer. It loops over those to pull out any of the Laravel-specific information and ultimately returns that configuration. So let's take some methods from this class and build our own task manifest. So I'm going to copy a few of these that I think that I'll need. And let's go back to our project and we'll make a new catch-all support folder. And within that, I'll make a new task manifest. All right, let's copy those methods in here and let's bring them to life for our project.

Update Run Command Registry2:53

And within that, I'll make a new task manifest. All right, let's copy those methods in here and let's bring them to life for our project. All right, starting at the top, we probably will want a manifest that we track ourselves. So I'll go ahead and create that property. Let's make it an empty array. So if there is a manifest, we'll go ahead and return that. Otherwise, we need to check to see if a manifest exists. So let's make another property for this path. It'll just be a string, but we don't actually know what this path is yet. So I'm probably going to need to make a constructor where we set this at runtime.

It'll just be a string, but we don't actually know what this path is yet. So I'm probably going to need to make a constructor where we set this at runtime. So we'll bring in the manifestPath here to set that. And let's get rid of these doc blocks. Again, I don't really feel that they're necessary when I'm just doing something simple like setting a property and all the same information can be inferred from the type hints and the code itself. So let's keep going. Now that we build this up, we'll see that if it's already a file, we'll just load that now. I don't want to reference the file system directly. So instead, we could probably just return the requiring of that manifestPath directly. So let's just get rid of that file system dependency.

So instead, we could probably just return the requiring of that manifest path directly. So let's just get rid of that file system dependency. I don't think we're really going to need it. OK, again, let's get rid of these doc blocks for now. They're not even ours anyway. We copied the code. Same thing here. Let's get rid of files. We'll just say if file exists. Now, the vendor path is not something that's defined either.

We'll just say if file_exists. Now, the vendor path is not something that's defined either. So we'll probably want to pass that in. I'll go ahead and add that property as well. We'll say that it's a string. And let's pass that in as a constructor parameter as well. All right, moving right along. Again, we can change this to file_get_contents. And this should now give us a list of packages pulled out from this installed.json file. OK, we're not going to be ignoring anything.

And this should now give us a list of packages pulled out from this installed.json file. OK, we're not going to be ignoring anything. So we don't need to support that type of behavior. So let's go ahead and get rid of anything related to that. That's really this whole entire middle section here. So now we're going to run a collection pipeline over each of the packages and map those into an array of the package name with the configuration for Laravel. So let's customize this inner portion for our use. I don't think we need this structure. So I don't think we need map with keys.

I don't think we need this structure. So I don't think we need map with keys. We can probably just get away with map and directly return the configuration for that package. And since it's not going to be Laravel, it's actually going to be shift. And the format of this configuration needs to follow what we've done in our run command. So within a package that contains automated tasks, we would expect it to have some kind of format in its composer.json file of extra with a shift key that has a mapping of the task name followed by a reference to the class that has that task. If that configuration doesn't exist for the package,

followed by a reference to the class that has that task. If that configuration doesn't exist for the package, then we would just return an empty array, which gets filtered out. Now, it doesn't look like we copied over the right method. So let's go see what that does. All right, it looks like this checks to make sure that the paths are all present and writable. And it ultimately outputs the manifest as PHP code using var_export. So I think we'll want this method. Let's bring it over to our own task manifest. And we didn't end up using format.

Let's bring it over to our own task manifest. And we didn't end up using format. So I think we can get rid of that. And let's bring this to life for our task manifest. So we'll bring in that exception. And again, we don't need to replace files, we'll just do a file_put_contents. Okay, this should be everything we need for the task manifest. Now we actually need to call this class not only from the discover command, but ultimately in our run command to get the actual manifest. Since this should behave pretty similar to Laravel's discover command,

Wire Command to Manifest6:54

but ultimately in our run command to get the actual manifest. Since this should behave pretty similar to php artisan discover command, let's see if we can copy some code from it as well. So let's take a look at the package discover command. All right, there's our package discover. And it looks like it just called build on the package manifest. So again, we should be able to do the same thing in our code. Let's bring this across. And instead of this package manifest, let's use our task manifest. Now we actually don't have a reference to this manifest property.

And instead of this package manifest, let's use our task manifest. Now we actually don't have a reference to this manifest property. And build actually doesn't return anything. So let's make a manifest list to get all of that back, which I think really could just be this getManifest method, which we're actually not even using anyway. So let's just make these one for now and see where it gets us. All right, we're going to loop over those, we're going to loop over the keys. And the key is actually no longer a description, it's going to be the task. And we'll output the components task with that task.

And the key is actually no longer a description, it's going to be the task. And we'll output the components task with that task. This should give us an output pretty similar to what we get when discovering packages. But now it's going to be for discovering tasks. The final thing we need to do is not only use the registry or manifest in our run command, but we need to make sure that we're registering our own default classes. So let's update our run command to have a constructor, which injects our new task manifest. So we'll say task manifest, task manifest, and we'll make sure to initialize those properties.

So we'll say taskManifest, taskManifest, and we'll make sure to initialize those properties. And now if we go back down to our taskRegistry, we should instead be able to get everything from our taskManifest list. All right, now for making sure that we load in our default commands. And we should be able to do that by merging in our defaultTasks. We'll just make that an additional method which returns just that, our defaultTasks. All right, let's see how this all works. Let me just get rid of this lingering comment here. So we'll say php shift discover.

Bind Manifest and Debug9:17

Let me just get rid of this lingering comment here. So we'll say php shift discover. Okay, looks like we have a container issue here. We were expecting the task manifest to get things like the manifest path and the vendor path, but we haven't registered any of those bindings. So let's go into our AppServiceProvider and we'll do exactly that. So this app, and since this is an expensive operation looping through all the packages, we want this to be a singleton. So we can make sure that that manifest is cached once per run. So we'll say taskManifest class, and let's make the function callback.

So we can make sure that that manifest is cached once per run. So we'll say TaskManifest class, and let's make the function callback to return a new TaskManifest and pass in all of those appropriate paths. So what does TaskManifest take? It takes the manifest path and the vendor path. In thinking about this a little bit more, I think we only really need the vendor path. The manifest path is really just the vendor path followed by some kind of file name. And we could just use something like shift tasks.php. All right, so how do we get the vendor path? If we go back and look at the PackageManifest,

All right, so how do we get the vendor path? If we go back and look at the package manifest, I think they were doing some env magic to figure out what that potentially was. So again, let's just copy this from Laravel so they behave the same way, and we'll pass that in here using env. Now, we don't have access to this basePath variable, but I think we could use the same thing we've been using in our scripts, which is git current working directory. All right, let's go back to the command line and try to run this again. All right, looks like another error class command not initialized correctly.

All right, let's go back to the command line and try to run this again. All right, looks like another error class command not initialized correctly. Oh, of course, we need to call the parent constructor. Good error message there. So let's call parent::__construct, and there we go. Even phpStorm's happy now. All right, let's run that again. We didn't seem to get any output. I would have expected to see at least our three default tasks. All right, my guess is merge isn't working exactly the way I thought.

I would have expected to see at least our three default tasks. All right, my guess is merge isn't working exactly the way I thought. So let's go ahead and run a dump here to see what's going on. Okay, it looks like we're getting an empty array from all the different dependencies that are within the vendor folder. Maybe we should put this dump after we filter. So let's put it here and see what it looks like. Okay, we do get our array of three items, so why aren't we getting that in our output? Let's dump the keys here as well.

so why aren't we getting that in our output? Let's dump the keys here as well. All right, interesting. It looks like the keys are blank. All right, I think the issue is the format, not only of our collection, but maybe what's expected to be in this extra configuration. Let's get into this installed json file and see if we can add our own kind of custom configuration. So if we just use Laravel 0 as an example and look at their extra, we would theoretically want to have something that might look like this. All right, this should start showing up when we run Discover.

we would theoretically want to have something that might look like this. All right, this should start showing up when we run php artisan discover. And we get this foo task, but notice it's underneath tasks and it's got its own high-level key. The format needs to be flattened and we need to be pulling in the tasks directly, not the entire configuration. Jumping back to our task manifest, we should be able to fix this pretty quickly. So instead of pulling in the entire configuration,

we should be able to fix this pretty quickly. So instead of pulling in the entire configuration, let's pull just the tasks. Let's also merge this after we filter the tasks. And I'm going to go ahead and remove dump because I'm optimistic we're going to get it to work this time. And finally, we could do something to flatten and rekey this, but we could really just go back to map with keys because this task configuration is already keyed with the task name and the class that we want to use.

because this task configuration is already keyed with the task name and the class that we want to use. All right, let's jump over to the Discover command and also kill that dump. And if we go back to the command line and run Shift-Discover, there we have it. We're discovering our task as well as our default task. This means that anyone, anywhere can now create automated tasks and they can be automatically discovered to use in your projects.

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