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

Adding Customers UI0:00

Let's expand our Faker app and add the ability to create fake customers in addition to fake products. Before we deal with the API and the back-end, let's first take care of the UI. We could either add a separate page to allow the user to create fake customers and set up the client-side routing using React Router. We could also use the tab component from the Polaris library and have multiple tabs, one for products, one for customers, and so on. Or we could simply add another slider within here just to allow the user to select numberOfCustomers in addition to numberOfProducts. I'm going to go with another slider because I think that's easier to implement and it's also better in terms of user experience. That way the user doesn't have to navigate between different pages or different tabs, they can just select all the options they want in here and click create and it's going to create all that fake data for them. Now to make

to navigate between different pages or different tabs, they can just select all the options they want in here and click create and it's going to create all that fake data for them. Now to make this work we're going to have to do some changes in our components. First of all we should probably get rid of the wording that makes this page seem like we are only creating products because right now this button for example says create 25 products. Instead if we're going to add another slider here for the customers we should make this button say something generic like generate data or create data or something like that so that it's not tied only to products. So let's open our code and the first step here is to rename this component because this component again is tied specifically to products and it's not going to be only about products, it's going to be about data generation. So let's open our project files, scroll to our react components, let's rename this

Refactoring UI Options1:34

specifically to products and it's not going to be only about products, it's going to be about data generation. So let's open our project files, scroll to our React components, let's rename this to fakeDataCreator, I think that makes more sense. Then let's rename the button here from createXProducts into something like generate. Let's also adjust the validation error here, instead of it saying failed to create fake products, let's just say failed to generate fake data. Then let's duplicate the slider here, so we'll duplicate this slider, let's call this numberOfCustomers. We should also probably make the minimum to zero because the user might want to create only fake customers or only fake products, so we can set the minimum to zero. Then we need to adjust the options state because right now it contains only the count of products, so instead maybe we can have something like productsCount and customersCount. Let's add

Then we need to adjust the options state because right now it contains only the count of products, so instead maybe we can have something like productsCount and customersCount. Let's add these options into the object in here, so this will be productsCount and by default this will be set to zero, so let's change this to zero and let's add customersCount with zero. Now we need to adjust the handleProductCountChange and rename this to just handleCountChange and we'll need to get the name of the field that we can replace, so the name of that field has to match to this to make this easier. This actually passes the name of the field as the second argument here, so we can accept it this way and set it this way. The only thing is that we need to make sure to set the id of the sliders which sets the name of the input field as well. So let's copy this, scroll down to our components, we'll replace this with this and we'll also add the id prop in here.

the id of the sliders which sets the name of the input field as well. So let's copy this, scroll down to our components, we'll replace this with this and we'll also add the id prop in here and set it to productsCount and we'll copy it and put it here and call this customersCount. All right, another thing that I want to adjust is the error handling of our catch on this Axios call. Right now we are only handling the status code of 422 but what if the status is 500 or something else? So we can do else case here and we can set the errors the same way except that we'll set the errorMessage to something like oops something went wrong, please try again later. All right, I think this looks good and given that our fakeDataCreator component is becoming a little bit bloated, why don't we extract this whole thing actually into its own hook or into its own component. I think using a hook here makes more sense. So let's create a new

Extracting Generate Hook4:19

is becoming a little bit bloated, why don't we extract this whole thing actually into its own hook or into its own component. I think using a hook here makes more sense. So let's create a new hook within the hooks directory, we'll call this useGenerateFakeData.js. Let's set this component up, we'll do const useGenerateFakeData, we'll return some object and we'll export default useGenerateFakeData. Now let's copy this whole thing and paste it in here, we'll call this generate and we'll return this from our hook and we can actually get rid of the useCallback in here because I don't think we need that and instead we're going to accept options as an argument because we need these options. So we'll do options and we'll remove this from here. All right now let's change a couple of things in here. So first we need the Axios instance, so we'll do const Axios equals useAxios. Next we can rename this to something more generic like loading, so

right now let's change a couple of things in here. So first we need the Axios instance, so we'll do const axios = useAxios(). Next we can rename this to something more generic like loading, so we'll do setLoading and change it here and here and we can actually return that loading state as part of this hook. So we'll do const [loading, setLoading] = useState(false). Next we need to handle the errors as well, so let's create that state in here, [errors, setErrors] = useState([]), this will be set to empty array by default. Next we also need a state for the toastMessage, so we'll do const [toastMessage, setToastMessage] = useState('') and set this to empty string by default and let's return these things from this hook. So we have generate, we need loading, we need toastMessage and we need the errors. Let's now adjust our fakeDataCreator so we can actually get rid of all of this from here and let's add that in here. We'll do const generateToastMessage

toast message and we need the errors. Let's now adjust our fake data creator so we can actually get rid of all of this from here and let's add that in here. We'll do const generateToastMessage loading errors and we'll get that from our useGenerateFakeData. We can remove these states because we no longer need them, we also don't need this Axios and we can get rid of this as well and instead we can call our loading that. Let's format the code and let's adjust our button on click handler to call this generate function. So let's scroll down, we'll call this function and pass options as an argument. Next thing that we need to adjust is this setToastMessage and setErrors, so we need on dismiss handlers for the toast and for the errors. So why don't we take this away from here and instead add some kind of on dismiss handler to be returned from our custom hook. So we'll call something like dismissToast and in here we'll call something like dismissErrors. We

from here and instead add some kind of on dismiss handler to be returned from our custom hook. So we'll call something like dismissToast and in here we'll call something like dismissErrors. We can then get these from our custom hook, so we'll do dismissErrors and dismissToast. Let's format the code, let's go into this hook and we'll need to add these functions in here. So let's paste in this code, we'll set this to a variable const dismissToast. Let's duplicate that, we'll do dismissErrors and set this to setErrors to an empty array. We can then return them here, dismissErrors and dismissToast and I think this is good enough. Note that there are many different ways to structure this and possibly even better ways to structure this and I am not an expert in React, so just bear with me as I write some of this React code and if you know better ways to write it feel free to structure it your own way. So let's test this out quick to make sure that we didn't break.

just bear with me as I write some of this React code and if you know better ways to write it feel free to structure it your own way. So let's test this out quick to make sure that we didn't break our UI and seems like we did break our UI. Let's open dev tools and we're getting invalid hook call. I may not be returning something properly from the hook, so let's open useGeneratedFakeData and yeah this this is the issue here. So let's get rid of that, that's a typo, let's try it now and sure enough we have two sliders. We can increase the slider on this, increase the slider on that, that seems to be working. One little adjustment that I would make is that I would maybe add this count into the label in here, so that way it's super clear to the user how many products or how many customers they're about to create. So let's go back to the code, go to FakeDataCreator component and in here we can add the number of products in parentheses or something

products or how many customers they're about to create. So let's go back to the code, go to FakeDataCreator component and in here we can add the number of products in parentheses or something like that. So we can do it something like this and do options.productsCount and we can do the same thing for the customers and change this to customersCount. Let's save, go back to the browser and sure enough that works. However if it's zero maybe we can get rid of that. So let's go back to the code, let's do if options.customersCount is greater than zero then show the parentheses plus and plus and we can remove the parentheses from here and from there. Otherwise we can just show empty string and we can do the same thing in here. Let's replace this here, replace that here and I think this is good enough. Let's go back to the browser and that works. If we go down to zero nothing shows. Let's now adjust our routing and controller. So let's go back to the code, we're

Updating Routes and Backend9:59

and I think this is good enough. Let's go back to the browser and that works. If we go down to zero nothing shows. Let's now adjust our routing and controller. So let's go back to the code, we're going to change the route name that we make the request to. Instead of /products we can change this to something like fakeData because we're no longer just creating products, we're creating and generating fake customers as well. Then let's open our web.php file, we'll change this to fakeData, let's change this to fakeData as well. We need to rename our controller so let's open our files, rename ProductController to something like FakerController. Then if we go into this method the other thing that we need to adjust is probably this CreateProductRequest. Yeah we need to change this to productsCount and duplicate it and change it to customersCount. The minimum is zero for both, they both are not required so we should set this to nullable and actually maybe

to change this to productsCount and duplicate it and change it to customersCount. The minimum is zero for both, they both are not required so we should set this to nullable and actually maybe we should have some kind of custom rule here that only one of them is required because if this is given and this is not given that should be okay but if both of them are not given then we should throw an error. We could do this either by creating a custom rule or by using a closure. I'm going to use a closure here just for simplicity so we'll do function string attribute mixed value and closure fail and we'll check here if the value is less than or equal to zero and the customersCount is also less than or equal to zero then we'll trigger the error. So we'll do this getCustomersCount less than or equal to zero then we'll trigger the fail and set the error message to something like slide either number of products or customers to at least five to proceed. Now let's also rename

less than or equal to zero then we'll trigger the fail and set the error message to something like slide either number of products or customers to at least five to proceed. Now let's also rename the CreateProductRequest to something like CreateFakeDataRequest so let's change this to CreateFakeDataRequest and I think we can test this out so let's open the browser let's click on generate when both of them are zero and seems like we're getting an error did I forget to import axios let's go in here uh no we have axios in here axios.post oh this supposed to be an object like this all right this should work now let's test this out let's go back to the browser let's click generate and sure enough we're getting the correct validation error now if I set this to 10 and click generate this error message is going to go away however we're going to get a different error message because we haven't adjusted the controller so it's probably going to trigger some

10 and click generate this error message is going to go away however we're going to get a different error message because we haven't adjusted the controller so it's probably going to trigger some 500 error so let's click on generate and as you can see we're getting that oops something went wrong please try again error let's go back to the code and adjust the controller so that this works we'll need to change this to dataProductsCount and actually we only need to dispatch this job if the productCount is given so let's do something like productsCount equals $this otherwise 0 let's do the same thing for customersCount and change this to customersCount and we'll do it this way and then in here we'll add a simple conditional that if the productsCount is given and is greater than 0 only then we'll trigger this and then we'll do the same thing here for customersCount if it's given then we'll dispatch some CreateCustomersJob so let's copy

count is given and is greater than zero only then we'll trigger this and then we'll do the same thing here for customersCount if it's given then we'll dispatch some CreateCustomers jobs so let's copy this put it here we'll change this to createCustomers and change this to customersCount and we can actually assign this to $user as well so let's do it this way $user $user let's adjust the destroy method as well we'll need to duplicate this and call this deleteCustomers and let's do the same thing here let's set this to $user this way and replace this with $user as well now you could of course do these different ways depending on your structure and preference for example in this case we might be running two jobs in parallel essentially doubling the api requests so you might hit the rate limits quicker so you might want to run these jobs in sequence or in batch all right so let's now create these jobs so i'm going to open the jobs directory and actually duplicate

hit the rate limits quicker so you might want to run these jobs in sequence or in batch all right so let's now create these jobs so i'm going to open the jobs directory and actually duplicate createProducts and call it createCustomers and let's duplicate the deleteProducts and call it deleteCustomers now i'm going to comment this part out for now because we're not implementing that just yet let's do the same thing in here so we'll just comment this part out let's make sure to import these jobs in our controllers so let's import this import this and let's test this out to make sure that this works let's go to browser click on generate and the error message is gone and we see the toast message stating that it has started generating the data now let's switch to Telescope and sure enough we see that job right here we don't have the createCustomers job because we didn't select that job in the slider let's go back to the code let's select this to 25 click

to telescope and sure enough we see that job right here we don't have the createCustomers job because we didn't select that job in the slider let's go back to the code let's select this to 25 click generate and sure enough we have createCustomers job let's test the delete part let's click deleteFakeData we're not getting the toast message oh we're getting 404 uh i forgot to adjust the route in our deleteComponent because this still makes the delete request on the /products this should be /fake-data let's go back click delete and sure enough we're getting deletingFakeData let's switch to telescope and we're getting two jobs queued one to deleteProducts and the other to deleteCustomers i don't have the queue running so that's why it's in pending but you can see that the jobs are being queued all right so now that we have both customers and products why don't we adjust the api calls to actually create the fake customers so let's go to

Implementing Customers API16:26

but you can see that the jobs are being queued all right so now that we have both customers and products why don't we adjust the API calls to actually create the fake customers so let's go to the Shopify's Admin REST API documentation open the customers in here click on customer resource we see right here that multiple access scopes will be needed depending on which endpoints we're going to use if we click on the create customer here we see that this one requires customers access scope and actually most of these endpoints require customers access scope maybe the one with the orders will also require access to the orders access scope in our scenario the customers access scope should be enough so that means that we should update our Shopify app configuration and add the new required scopes so let's go to the code open Shopify app scroll down to the API scopes and we can either add the default scopes in here or just take this environment variable and create

add the new required scopes so let's go to the code open shopify app scroll down to the api scopes and we can either add the default scopes in here or just take this environment variable and create it in our .env file so let's open .env let's scroll down we'll add it right here and let's set this to read and write products and we'll also set it to read customers and write customers now let's adjust the api call to create and delete customers so in our create customers job let's uncomment this code the endpoint is customers.json and this will be customer and the resource has to be customer as well so we'll replace it to customer resource and replace it here this will be customers let's replace it here as well and we'll need to create the fake customers relationship as well as the model and migration but we'll do that in a couple of minutes so let's change this to fake customers this can stay the same except that instead of product it will be customer and as

the model and migration but we'll do that in a couple of minutes so let's change this to fake customers this can stay the same except that instead of product it will be customer and as far as the customer resource will be very minimal and just create customer with firstName and lastName now you can add more information if you want to you can read the documentation and see what fields they support but i'm just going to keep it super simple so we'll do firstName equals to something and lastName equals to something now i don't want to use the numeric indexes like we did with the products instead i want to create some fake firstName and lastName thankfully Laravel has the Faker that we could use to generate fake names so we'll do $faker equals Faker\Factory::create() and then in here we'll do $faker->firstName() and $faker->lastName() we could probably do the same thing for the products and the only thing is i don't know if Faker has any fake product related

and then in here we'll do faker firstName and faker lastName we could probably do the same thing for the products and the only thing is i don't know if faker has any fake product related uh data but you could always create your own extension or search the internet if there is already some extension for the faker that provides fake product names you could even expand this farther if you wanted to and have the ai do that job so maybe make an api request to the openai or something like that to generate fake product data all right so let's now create the fake Customer model migration and this relationship so let's open the terminal we'll do vendor/bin/sail artisan make:model FakeCustomer -m let's open the migration so we'll open fake_customers_migration this will pretty much have the same columns as the fake_products_migration so let's open fake_products_migration and just copy these things let's run the migration so we'll do

this will pretty much have the same columns as the fake_products so let's open fake_products and just copy these things let's run the migration so we'll do vendor/bin/sail artisan migrate that worked let's open the FakeCustomer let's also open the FakeProduct because it will be the same thing let's copy this paste it in here and let's also add the relationship to the User so let's open User we'll duplicate this this will be fake_customers FakeCustomer and i think we're good to go let's close this out close that out and let's adjust the DeleteCustomers so let's uncomment this we're going to change this to customers and then this will be fake_customer let's replace this with this fake_customer and this will be fake_customers let's change it here as well and i think we're good to go all right so let's go back to the browser and let's now generate 10 products

Handling Scope Approval Error20:55

with this fake Customer and this will be fake customers let's change it here as well and i think we're good to go all right so let's go back to the browser and let's now generate 10 products and 5 customers let's click generate and it has started generating the fake data let's switch in here the jobs should have been queued they're in queue createProducts has processed successfully but createCustomers failed let's find out why this failed so let's inspect this scroll down we have the exception message right here and the exception states that the POST request to the customers json resulted in 403 Forbidden it also states that this action requires merchant approval for writeCustomers scope now you might be confused a little bit here because we've added this writeCustomers scope in our configuration right however even though we did that the merchant or the owner of the shop has not approved that scope we need to somehow force the user to be

this right customer scope in our configuration right however even though we did that the merchant or the owner of the shop has not approved that scope we need to somehow force the User to be redirected to that grant page where they can review the permissions again and approve it from the beginning once they approve it then Shopify registers that basically that the merchant has approved this scope for this app and it's not going to respond with 403 now there are different ways that we can do this redirect we can introduce a flag on the User model something like forceScopeUpdate and flip it to 1 and then have some kind of middleware that checks that flag and if it's true then redirect the user to the authenticate route so that the user accepts the new scopes let's do that in the next episode

let's do that in the next episode

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