DEV Community

Cover image for What I learned from the Ecommerce Website I built using Vue, PHP and MySQL(school project)
Marlo Badinas
Marlo Badinas

Posted on

What I learned from the Ecommerce Website I built using Vue, PHP and MySQL(school project)

Intro

What did I build?
The project Bossing's, an e-commerce website where users can browse shop products, add items to their cart, and checkout. The site includes a simple admin dashboard for easy order management.

Functions:

  • Sign-in and sign-up functionality.

Sign in page

  • Add to cart and checkout functionality.

Product page

Cart page

  • Admin dashboard that displays a summary of details and a list of orders in the shop.

Admin dashboard page

  • Inventory page that helps manage stock effectively and edit shops product details.

Inventory page

Background

This project was developed as a school requirement, we were told to build an e-commerce website demonstrating CRUD (Create, Read, Update, Delete) functionality using native PHP, an Apache server, and MySQL. I chose to create an online shop for Bossing's Chili Garlic Oil Sauce simply because the resources (images, data) are easily accessible due to the shop's proximity to my location, and familiarity of the business process flow.

What I learned

Frontend image
Frontend:
Stack: Vue, and TailwindCSS

Vue
I chose Vue.js for the front-end framework due to its simplicity and intuitive coding experience. Among the available framework options, Vue is the only option I considered, primarily because I had prior experience with it and wanted to apply the little knowledge I had with it in a practical project.

While building the project, I encountered lots of bugs and errors. Since I was still learning Vue as I code, these issues opened knowledge gaps that I addressed through research and community support. I had to refer to different sites even to make different programs work because I lacked the prerequisite knowledge and have wished someone had already answer.

For example when I was referencing static assets, such as images. I needed to display a logo on the home page, but my attempts to use various URLs resulted in 404 (Not Found) errors. Using local images in Vite, the bundler I was using, is more confusing than I thought. The simplest solution that worked for was importing each image as a variable, which I could then reference in the template. Here’s the code snippet I used for reference:

<script setup>
import imgUrl from '@/assets/img.png';
</script>

<template>
  <img :src="imgUrl" />
</template>

Enter fullscreen mode Exit fullscreen mode

The @ symbol refers to the src folder. While this method works, an obvious drawback is the need to import each image individually if there are multiple images.

On the product page, I was supposed to dynamically load the products image, and the fetched product data was supposed to contain image_src as a string to reference the image URL. However, this approach was not functioning correctly. To resolve this, I changed the value of each product’s image_src to its corresponding name. I then used a helper function, getImageUrl, to dynamically load the images based on the product’s image source name. Here’s the implementation:

import classic from '@/assets/img/classic.png';
import sweet from '@/assets/img/sweet.png';
import extra from '@/assets/img/extra.png';


function getImageUrl(name) {
  if (name === 'classic') {
    return classic;
  } else if (name === 'sweet') {
    return sweet;
  } else {
    return extra;
  }
}
Enter fullscreen mode Exit fullscreen mode

In the template, I called this helper function in the src binding:

<div v-for="product in products" :key="product.id" >
  <div class="h-80">
    <img :src="getImageUrl(product.image_src)" :alt="product.image_description" />
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Vue’s reusable components made the project easier to build and customize. However, I regret not fully leveraging this feature. Because I did not consider the project's scalability, I missed opportunities to componentize several reusable HTML codes. If I were to redo the project with scalability in mind, I would create more reusable components for future maintenance.


Backend image

Backend:

Stack: PHP, Mysql, Axios

PHP
The web API endpoints that I built using native PHP were messy, and unorganized but it did it's job. I have multiple endpoints inside and in every endpoint,there should be a connection from the database. A simple solution I implemented, which I copied from one of the tutorial video I watched, was creating a separate config.php, and a Database.php . The configuration file stores the database connection details, such as the host, port, and database name. The Database.php class accepts the configuration, username, and password as parameters during instantiation, and uses these to create a PDO database connection. Additionally, this class provides helper functions for querying and fetching data.

Example usage:

Sample endpoint using the database instance

As you can see, in one of my endpoints, I only needed to require the two php file and create a Database instance to have a connection to the database.

User Access
I implemented a simple user level check in the client side to direct users to their respective view pages after logging in. Here's how it works:

if (data.userLevel === 1) {
  router.push('/dashboard');
} else {
  router.push('/home');
}
Enter fullscreen mode Exit fullscreen mode

After the user logs in, their user level is checked. If the user's level is 1, they are redirected to the dashboard. If the user's level is not 1, they are redirected to the home page.

Data Validation
To ensure the input from the user is reliable, I implemented both client-side and server-side validation:
For the client-side, just simple check to ensure input fields are filled before submitting, while on the server-side, data sanitation to ensure the input data is clean. There were endpoints that lacks server-side validation, but the client-side validation was good enough for the project purposes.

Same-origin policy
Since my web API is hosted by Apache (on a different domain or port) and the client side is run using npm run dev (typically on a different port),when I was testing my API endpoints, I encountered a CORS error. The browser sees these requests as coming from different origins and blocks them to prevent security risks. This error stems from a security mechanism that browsers implement called the same-origin policy.The same-origin policy fights one of the most common cyber attacks, which is the cross-site request forgery. With the help of chatgpt the solution implemented are:

CORS issue solution implementation

As you can see above, the code checks if the request origin contains 'localhost'. If true, it sets the Access-Control-Allow-Origin header to allow requests from that specific origin. This allows the API to accept requests from any localhost port. I added these multiple lines of code in every endpoint and then that solved the issue.

Database Relationship

I struggled with how to implement the relationship between order items (cart items) and orders (displayed in the dashboard). What I ended up doing is use the order_id as a foreign key in the order items table. This allows multiple order items to belong to a single order. The logic follows this sequence, when a user adds a product to the cart, the order_id for that item is initially set to null. When the user clicks the checkout button, the order_id for each order item is updated based on the newly created order's order_id. To display orders and their associated order items in the dashboard, I fetch all orders first and then loop through them, fetching their associated order items using an inner join in the SQL query. However, one downside of this approach is that when a user removes order items from their cart page, the associated data also disappears from the admin dashboard. Regrettably, I haven't resolved this issue yet. This simple approach was the only solution I could think of at that moment.

Tech/frameworks I discovered and tried using

Axios
Thankfully I'm familiar with Axios, a widely-used library. It simplified working with REST APIs, making the processes much easier.

Pinia
I had the opportunity to work with Pinia, a state management library, because I needed a way to store the user_id of the current user upon logging in. Pinia's stores made this possible. Whenever I need to retrieve the current user_id,I simply access it from the store I defined.

Vue-Router
Vue Router is the official client-side routing solution for Vue. You configure routes to tell Vue Router which components to show for each URL path. It checks the URL and, depending on what's entered there, displays a different component on the screen. This allowed the project to smoothly switch between components.

Postman
I used Postman to manually test the API endpoints instead of always checking the inspect section of my browser. This helped me identify issues more easily and faster. Often, errors were caused by mismatched spellings between the URLs I passed to Axios and the actual PHP endpoints, or by the CORS issues I mentioned earlier.

Conclusion

It has been such a rewarding experience, after solving bugs, rendering views, and getting routes to work, it feels satisfying. I'm also learning on the go with the tech stack I'm using for the current project, and I realized that I learn and master a technology faster when I'm actively using it. Encountering bugs while coding reveals knowledge gaps, which I can then fill through research. This simple project was far from being perfect, and there's a lot of improvements that I could have done, and I'm eager to implement those improvements in the future projects.

Here are some resources I referenced when writing this:

Top comments (4)

Collapse
 
drfcozapata profile image
Francisco Zapata

CONGRATULATIONS MARIO!!!!
As you rightly say, we discover our gaps in coding by coding, just like you learn to swim by swimming.
I work with Vue several years ago and worked for a while with React (and I'm learning Angular), but I don't change it; for me it's the best JS framework. Since more than 1 year I work also with Laravel and MySQL, so I know well the issues you faced.
Remember that when you store an image, what is recorded in the MySQL table is only the URL that indicates where it was stored, so the work with images in the front end with Vue is done by referencing those URLs.
Referencing images directly is very "costly" in terms of performance, and very unscalable. Here you had only 3 images and you were able to get around it with if-else, but what if it had been 100?
It really is simple to handle, and Vue makes it very easy to handle.
I suggest you add TypeScript to your Vue learning and you will see how powerful your code becomes.
Again CONGRATULATIONS bro and thanks for sharing your experience. Blessings from Venezuela.

Collapse
 
moobroobloob profile image
Marlo Badinas

Thankyou for your feedback, Francisco! I'm happy to hear that you found the blog post relatable. Yeah, I completely agree that storing only the URLs in the database is far more efficient and scalable approach, I'll definitely implement that on my future projects. Also, I already had my eye on learning Typescript as I have heard good things about it! So again thankyou for the helpful tips and for also validating the struggle I encountered with the current tech stack. :)

Collapse
 
msulaimanmisri profile image
Muhamad Sulaiman

Hi @moobroobloob . Thank you for your sharing your experience.

Just quick question. When you choose PHP, why you choose to built it using Native PHP rather than choose PHP CMS or Framework?

Collapse
 
moobroobloob profile image
Marlo Badinas

Hey, thanks for reading. Since this is a school project, we've been specifically instructed to use native PHP without any frameworks on top of it. For the front-end, however, we have the option to use a framework to make designing easier. I also think it's nice to get some hands-on experience with vanilla PHP before diving into frameworks! :)