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

Creating Controller and Request0:00

Let's create a ProductController so that we can replace this with the proper controller method and actually start creating the fake products. So let's open the terminal, we'll run vendor/bin/sail artisan make:controller, and the controller name will be ProductController. Let's also create the FormRequest class, so we'll do php artisan make:request CreateProductRequest. Let's replace this with the controller here, so we'll do ProductController, and the method should be store. Let's import this, we'll go to the controller, and let's create that store method. So we'll do public function store, and this will have CreateProductRequest FormRequest. Let's create the data variable, which will contain the validated request parameters.

Adding Count Validation Rules0:41

So we'll do public function store, and this will have Create, ProductRequest, FormRequest. Let's create the data variable, which will contain the validated request parameters. Alright, let's go to our FormRequest class, and we're going to get rid of that. And the only rules that we need to define is for the field count, because that's the only field that we currently have. So we'll do count, and the count is a required field, it is an integer field, and it has to be minimum 5 and maximum 100. Let's actually test the validation part. So what I'm going to do is that I'm going to set the minimum to 10 and maximum to 95, that way I can select 5 in the slider, and it should trigger the validation error.

So what I'm going to do is that I'm going to set the minimum to 10 and maximum to 95, that way I can select 5 in the slider, and it should trigger the validation error. So let's open the terminal, run vendor/bin/sail npm run dev to start the VEED server. Let's go to the browser, open DevTools, click Create 5 Products, and we're getting 422 status code, which is expected, and the response contains the validation errors. Now we don't really have any validation error handling on the front end, so let's add some simple validation handling on the front end, maybe display the error messages in some kind of banner. So let's switch back to the code, go to ProductCreatorComponent, we'll create a new state here to store the error messages.

Handling Validation Errors UI1:59

So let's switch back to the code, go to ProductCreatorComponent, we'll create a new state here to store the error messages. So we'll do errors, setErrors, and initialize it with an empty array. Then what we can do is that we can add the catch clause here to catch the validation errors. So catch error, and then if the error.response.status is 422, then we can set the errors this way, error.response.data.errors. Now Laravel returns errors structured in a specific way, so if we go back to the browser, we see that structure right here. We have the field names as the keys, and the elements are basically the list of errors.

we see that structure right here. We have the field names as the keys, and the elements are basically the list of errors specific to that field. I want to basically flatten the nested structure and create a flat array containing all the error messages. That way we can display them together in some kind of list within the banner. So to flatten the structure in JavaScript, we're going to use Object.values and then flatMap functions. So we'll do Object.values, which will return the values of this object in an array format, and then do flatMap to flatten it.

So we'll do object values, which will return the values of this object in an array format, and then do flatMap to flatten it. Let's also add some handling here in case there are no errors. So we'll do or an empty object, and let's add handling for undefined values just in case. Then to actually display these errors, we need to create some sort of component. We can use the Banner component from the Polaris, but I want to wrap that within a custom component so that we can pass list of errors as a prop and have that component be responsible to render the banner with all the errors. So let's create a new component here.

render the banner with all the errors. So let's create a new component here. We'll call this ValidationErrorBanner JSX. Then let's create the variable errors here, which should accept the list of errors as a prop and is going to return a Banner component. Now within this Banner component, we can basically display all the errors in an unordered list structure. So we'll do unordered list, then we'll do errors.map, and for each error, we'll display the list item. All right, let's add the export default here, and we should probably customize our Banner.

the least item. All right, let's add the export default here, and we should probably customize our Banner. So first, we're going to set the title. Maybe actually let's take the title as a prop, that way it is customizable. And we'll set the status to critical, and we should probably make this Banner dismissible as well so that a User can get rid of it once they see the error. So let's add on dismiss as well, and we'll accept that as prop as well. So let's just pass that down this way. Then let's go back to the ProductCreator component, and we'll render it after this Button component within the FormLayout, that way it has a standard spacing.

Then let's go back to the ProductCreator component, and we'll render it after this Button component within the form layout, that way it has a standard spacing. Now we should only render the ValidationErrorBanner component if we actually have the errors. So we could either use the ternary operator and check if the errors.length is greater than zero and only then render it, or we could do short circuit evaluation using the logical AND operator. So we could do errors.length and ValidationErrorBanner component. And we need to pass the title here, so the title will be 'Failed to create fake products'. Then we need to pass the actual errors, so we'll pass it this way. And we need to pass on dismissHandler.

Adding Button Loading State5:36

Then we need to pass the actual errors, so we'll pass it this way. And we need to pass on dismissHandler. In this case, we're just going to set the errorState to an empty array. So we'll do function setErrors(), emptyArray. While we're at it, let's also add the loadingState to our button so that when the button is clicked, it becomes disabled and the user is not able to click it again. So we need a new state for that. So let's scroll up here, we'll call the new state something like creatingProducts. So creatingProducts, setCreatingProducts, and we'll set this to false by default. Then we can set it to true before making Axios requests.

So creatingProducts, set creatingProducts, and we'll set this to false by default. Then we can set it to true before making Axios requests. So we'll do set creatingProducts to true, and we'll reset it to false on finish and on catch. So if we catch any errors, we'll set the creatingProducts to false. And we'll also set it to false in here. Also, if the request is successful, we can set the errors to the empty array. So that way the errors banner disappears. So we can do set errors, []. All right, so let's test this out now.

So we can do setErrors, empty array. All right, so let's test this out now. Let's go back to the browser. Let's click on create five products again. And sure enough, we see this little banner listing all the errors in here. Let's select 100, click Create, and we get the proper validation error. We can dismiss this, click on that, and the banner is gone. We can click it again, and it comes back. All right, so the next step is now to actually create the fake product by making API call to the Shopify REST Admin API.

Building Shopify Product API Call7:09

All right, so the next step is now to actually create the fake product by making an API call to the Shopify REST admin API. So let's open the documentation, scroll down to products, click on product. And here we see the product resource definition with the available fields. If we keep scrolling, we get to the different types of requests, like we have a POST request to create a new product, GET request to retrieve a list of products, GET request to get the single product, PUT to update a product, and DELETE to delete the product. We're interested for now in the POST request, so let's click on that. And we can see some of the examples in here. We can even switch to PHP here and see the example.

And we can see some of the examples in here. We can even switch to php here and see the example. Now there is a difference. We won't be able to create the product using this SDK because this is a different SDK than the one that we're going to be using. Because Laravel Shopify already comes with the proper API clients that we can use to interact with this API. So let's go back to our code and build the product resource that we're going to create. So let's go to the controller. First we need to get the count from this data variable.

So let's go to the controller. First we need to get the count from this data variable. So we'll do count equals data count. Then we should create a simple for loop that will run this many times. So we'll do for i equals zero, i less than count, i plus plus. And within this loop, we're going to create a product because we're creating these many products. So let's create the product resource. This is going to be in array format for now, but you can use DTOs or transformers or whatever you need to use for your own project.

This is going to be in array format for now, but you can use DTOs or transformers or whatever you need to use for your own project. I'm just going to keep it simple for this series. Our resource, as I mentioned before, is going to be simple. We're just going to pass the body HTML, which will be product description, and pass the index here just so that we can see it gets created properly. And we'll set the title to product title and add the same index as well. Next we need to make the API call. We can get the API client instance to make API calls from the current User object. If we remember our User model in here implements the ShopModelInterface, and then we are

We can get the API client instance to make API calls from the current user object. If we remember our User model in here implements the ShopModelInterface, and then we are also using the ShopModelTrait. If we inspect this trait and scroll down, we see this method here, which returns the basic Shopify API instance that lets us make API calls with Shopify. It basically uses Guzzle client behind the scenes and makes it easier to interact with Shopify's API. It's a separate package that is a dependency to Laravel Shopify. So let's close this out, go back to the controller, $user equals request()->user(). And let's add a doc block here indicating that it's a ShopModel instance.

So let's close this out, go back to the controller, $user equals $request->user(). And let's add a doc block here indicating that it's a Shop model instance. In fact, why don't we rename this to $shop because in the context of Laravel Shopify, the $user is the $shop. Then we can make the API call this way. So we'll do $shop->api->rest. We need to pass the type of request as the first argument, which is 'post'. The second argument is the path or the endpoint and the endpoint we can get from the documentation. So we'll pass 'admin/api/products.json'. And as the third argument, we need to pass the payload or the parameters.

So we'll pass admin API products, Jason. And as the third argument, we need to pass the payload or the parameters. So we'll pass the product in here. Now this API call either returns a Guzzle response in array format with the JSON decoded body or it returns Promise, depending if you're making async or synchronous calls. By default, it makes synchronous calls, so it returns array by default. We can actually inspect this rest method call here, which uses the Rest class. If we scroll down here, we see the handling for both synchronous and asynchronous calls. So if it's an asynchronous call, it returns Promise, otherwise it returns either the result of handleSuccess or handleFailure, depending if the request was successful or not.

So if it's an asynchronous call, it returns promise, otherwise it returns either the result of handleSuccess or handleFailure, depending if the request was successful or not. Both methods, handleSuccess and handleFailure, return a structured array with errors key being either true or false. So we can use that to basically check if the request failed or not, and then try to get the body if errors is false. So let's close this out and let's go back to the controller. We'll assign this to a variable called response, and in here we can check if the response has the errors, then we'll simply throw the exception that is given within the response. Otherwise we'll extract the ID and the title and save it in some kind of array.

the errors, then we'll simply throw the exception that is given within the response. Otherwise we'll extract the ID and the title and save it in some kind of array. So let's extract the ID from the response. So we'll do response->body['product']['id']. And the reason I know this is because in the documentation, if we scroll down to an example response, we see that it contains the product resource if the request is successful. So we have the ID. Let's also get the title. So response->body['product']['title']. And we can save these in some kind of array.

So response body product title. And we can save these in some kind of array. So maybe like products or productsCreated and we'll add id id title title. For now, these are the only two things that we are interested in. We could save more information if we wanted to, but id and title should be more than enough. Let's create the products array in here, set it to empty array, format the code, and let's return this products array as a JSON response so that way we can see if it actually worked or not. To do that, I'm going to use the response factory injected in the constructor, but you could use response facade or the response helper function. I prefer the dependency injection.

response facade or the response helper function. I prefer the dependency injection. So we'll do construct private read only responseFactory from the Illuminate\Contracts\Routing, then scroll down and we'll do return $this->responseFactory->json and pass products this way. All right, so let's test this out now. Let's open the browser. Let's set to five products. Let's open the dev tools, click create five products, and we're getting the validation error. Oh, that's right. I forgot to revert the change that I made initially when we were testing the validation.

Testing Product Creation Response13:18

Oh, that's right. I forgot to revert the change that I made initially when we were testing the validation. So let's go back to the code, open the CreateProductRequest. We'll change this back to 5 and 100. Let's try it again. And we're getting a successful response with the response containing the 5 products that were just created. As you can see, we have the ID and we have the product title. We can actually confirm this by going to the products page because this is our testing store. Right. So we go to the products and we have the 5 products right here. All right. So the next step for us now is to keep track of the products that were created so that we could easily clean up and delete the products that were created by our application.

Right. So we go to the products and we have the five products right here. All right. So the next step for us now is to keep track of the products that were created so that we could easily clean up and delete the products that were created by our application. Let's do that in the next episode.

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