1. Mô tả đề tài
Surfside Media là một Website bán hàng về thời trang. Với giao diện giúp khách hàng dễ sử dụng và tiện lời với tìm kiếm thông minh giúp khách hàng dễ dàng tìm ra mắt hàng mình muốn nhanh chóng. Surfside Media cũng cung cấp đầy đủ thông tin về sản phẩm, đánh giá của người dùng giúp khách hàng có cái nhìn tổng quan về sản phẩm, các sản phẩm có lượt view cao sẽ được đề xuất lên trang chính khách hàng có thể dễ dàng nhìn thấy được. Đặc biệt dịch vụ hỗ trợ khách hàng đặt hàng và thanh toán online nhanh gọn giúp khách hàng có thể thanh toán trực tuyến qua thẻ tín dụng.
1.1. Lý do chọn đề tài
Trên thị trường hiện nay việc một số cửa hàng vẫn chưa xây dựng Website bán hàng cho cửa hàng mình vẫn còn nhiều từ đó gây ra một số khó khăn như: tiếp cân khách hàng tiềm năng, mất đi cơ hội kinh doanh online, cạnh tranh khó khăn với đối thủ, khả năng quảng cáo và marketting hạn hẹp vì thế nhận thấy được những khó khăn đó nên em quyết định xây dựng Website bán hàng Surfside Media giúp các cửa hàng có thể giải quyết được những vấn đề đó. Surfside Media giúp các cửa hàng có thể mở rộng môi trường kinh doanh, tiếp cận đến khách hàng rộng rãi hơn, dễ dàng quảng cáo và marketting cửa hàng của mình đến với khách hàng, xây dựng được một cửa hàng uy tín và có thương hiệu đáng tin cậy, tăng doanh số bán hàng so với bán hàng bình thường tại cửa hàng, tăng được tính cạnh tranh với các đối thủ.
1.2. Mục tiêu của đề tài
Trong thời đại 4.0 và chuyển đổi số như hiện nay việc xây dựng một Website bán hàng riêng cho cửa hàng mình rất quang trọng giúp cửa hàng bán hàng hiệu quả hơn, mở rộng được thị trường và tạo ra một trải nghiệm mua sắm nhanh chóng và tiện lợi cho khách hàng. Dưới đây là một số mục tiêu xây dựng web Surfside Media:
- Xây dựng một giao diện đẹp ,dễ sử dụng giúp khách hàng có một trải nghiệm tốt và dễ dàng tìm kiếm cũng như mua sắm các sản phẩm một cách thuận lợi nhất.
- Tạo ra nhiều danh mục sản phẩm đa dạng đáp ứng nhu cầu của đa dạng khách hàng.
- Xác định được các chiến lược quảng cáo thương hiệu của cửa hàng, thu hút lượng truy cập và chuyển đổi người dùng thành khách hàng thật sự.
- Mở rộng các tính năng của web.
- Tương thích đa nền tảng khác nhau như Google, Bing, CocCoc, Safari,…
- Hiệu năng tốt.
- Tối ưu hóa được chi phí.
- Bảo mật dữ liệu.
1.3. Mô tả bài toán
Website bán hàng Surfside Media là một web được phát triển để giúp quảng bá sản phẩm của cửa hàng và tăng doanh thu bán hàng. Bài toán này liên quan đến việc bán hàng, quảng cáo và xây dựng thương hiệu cửa hàng,… Dưới đây là một số tính năng và yếu tố chính của bài toán web bán hàng Surfside Media:
- Tìm kiếm sản phẩm: Giúp khách hàng tìm kiếm sản phẩm mà khách hàng cần mua.
- Danh mục sản phẩm đa dạng: lọc giá cả theo từng phân khúc giúp khách hàng có thể chọn lựa các sản phẩm theo như ý muốn.
- Trang Chi tiết sản phẩm: Cung cấp đầy đủ thông tin chi tiêt, hình ảnh, giá cả, đánh giá từ người dùng trước đó để giúp người mua có cái nhìn tổng quan về sản phẩm trước khi quyết định mua hàng.
- Trang Dasboad: Giao diện quản trị cho người quản lý cửa hàng có thể cập nhật, thêm mới, hoặc điều chỉnh thông tin về sản phẩm, người dùng và đơn hàng.
- Giỏ hàng: cho phép người dùng thêm sản phẩm vào giỏ hàng, điều chỉnh số lượng một cách dễ dàng.
- Giao diện thân thiện, dễ dạng sử dụng giúp khách hàng có một trải nghiệm tốt khi mua sắm.
- Mở rộng tính năng của website điều chỉnh cải thiện các tính năng của website cho người dùng có một trải nghiệm mua sắm tốt hơn.
- Tối ưu hóa hiệu suất chi phí: đám ứng mọi nhu cầu mà vẫn dữ được chi phí phù hợp.
- Bảo mật dữ liệu: đảm bảo thông tin khách hàng luôn được bảo vệ an toàn tạo độ tin cậy cho khách hàng.
1.4. Giới thiệu tổng quan về công nghệ, kiến trúc hệ thống sử dụng.
- Công nghệ: Hệ thống sử dụng các nền tảng nổi tiếng như Laravel, mysql…
- Kiến trúc hệ thống: Cấu trúc của một trang Surfside Media, từ máy chủ, cơ sở dữ liệu đến giao diện người dùng.
1.5. Các chức năng đã cài đặt được
- Tìm kiếm sản phẩm.
- Lọc sản phẩm.
- Danh mục sản phẩm.
- Trang chi tiết sản phẩm.
- Giỏ hàng.
- Trang Dasboard: quản lý và thêm, sửa xóa sản phẩm, user.
1.6. Kết luận
Website bán hàng điện máy Surfside Media không chỉ là một website để bán hàng mà nó còn mở rộng thị trường kinh doanh, tạo ra nhiều trải nghiệm tốt cho khách hàng với nhiều thuận lời và tiện ích cho khách hàng.
Website có giao điện dễ dàng sử dụng và các chức năng tìm kiếm thông minh, có đầy để thông tin của sản phẩm như giá cả, chi tiết sản phẩm, đánh giá của khách hàng trước đó là những điểm mạnh để thu hút khách hàng. Việc tối ưu hóa chi phí, bảo mật thông tin người dùng và tương thích với đa nền tảng là một điểm mạnh để có thể phát triển cửa hàng lên một tầm cao mới.
Trong thời đại chuyển đổi số như hiện nay việc tạo ra một website bán hàng là một lời thế mạnh giúp các cửa hàng có thể mở rộng thị trường bán hàng và tiếp cận đến người dùng giúp cửa hàng càng ngày càng phát triển hơn.
1.7. Những kết quả đạt được của đề tài
- Người dùng có một trải nghiệm tốt hơn, web dễ dàng sử dụng.
- Quản lý sản phẩm, thông tin khách hàng và các đơn hàng hiệu quả.
- Hỗ trợ tính năng tìm kiếm giúp khách hàng tìm và lọc sản phẩm mình cần mua nhanh chóng.
- Tương thích đa nền tảng có thể hoạt động trên nhiều nền tảng bao gồm cả điện thoại di động và máy tính.
- Giảm chi phí vận hành của cửa hàng.
- Mở rộng thị trường không chỉ ở 1 thành phố mà có thể rộng ra cả nước.
- Tăng doanh số bán hàng.
1.8. Hạn chế
- Bão mật dữ liệu vẫn còn kém.
- Độ ổn định của hệ thống vẫn còn gặp lỗi.
- Còn khó khăn trong quảng cáo và tiếp cận khách hàng.
1.9. Hướng phát triển
- Nâng cao tính bảo mật thông tin khách hàng, thông tin tài khoản và dữ liệu trang web.
- Quản lý được số lượng hàng đã bán và số lượng hàng còn tồn lại trong kho.
- Mở rộng danh mục sản phẩm.
- Phát triển thêm về mặt chăm sóc khách hàng.
- Tối ưu hóa hiệu suất.
- Nâng cao chất lượng sản phẩm và dịch vụ.
- Đưa vào sử dụng thức tế.
2. Mô hình dữ liệu
3. Model
category
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Category extends Model { use HasFactory; }
HomeSlide
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class HomeSlider extends Model { use HasFactory; }
Product
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use App\Models\Category; class Product extends Model { use HasFactory; public function category() { return $this->belongsTo(category::class,'category_id'); } }
User
<?php namespace App\Models; // use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; /** * The attributes that are mass assignable. * * @var array<int, string> */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for serialization. * * @var array<int, string> */ protected $hidden = [ 'password', 'remember_token', ]; /** * The attributes that should be cast. * * @var array<string, string> */ protected $casts = [ 'email_verified_at' => 'datetime', ]; }
4. Controller
<?php namespace App\Http\Controllers; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; class Controller extends BaseController { use AuthorizesRequests, DispatchesJobs, ValidatesRequests; }
ProfileController
<?php namespace App\Http\Controllers; use App\Http\Requests\ProfileUpdateRequest; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Redirect; use Illuminate\View\View; class ProfileController extends Controller { /** * Display the user's profile form. */ public function edit(Request $request): View { return view('profile.edit', [ 'user' => $request->user(), ]); } /** * Update the user's profile information. */ public function update(ProfileUpdateRequest $request): RedirectResponse { $request->user()->fill($request->validated()); if ($request->user()->isDirty('email')) { $request->user()->email_verified_at = null; } $request->user()->save(); return Redirect::route('profile.edit')->with('status', 'profile-updated'); } /** * Delete the user's account. */ public function destroy(Request $request): RedirectResponse { $request->validateWithBag('userDeletion', [ 'password' => ['required', 'current-password'], ]); $user = $request->user(); Auth::logout(); $user->delete(); $request->session()->invalidate(); $request->session()->regenerateToken(); return Redirect::to('/'); } }
5 Liveware
Admin
-AdminAddCategory
<?php namespace App\Http\Livewire\Admin; use Livewire\Component; use App\Models\Category; use Illuminate\Support\Str; use Livewire\WithFileUploads; class AdminAddCategoryComponent extends Component { use WithFileUploads; public $name; public $slug; public $image; public $is_popular=0; public function generateSlug() { $this->slug = Str::slug($this->name); } public function updated($fields) { $this->validateOnly($fields,[ 'name'=>'required', 'slug'=>'required', 'image'=>'required' ]); } public function storeCategory() { $this->validate([ 'name'=>'required', 'slug'=>'required', 'image'=>'required' ]); $category = new Category(); $category->name = $this->name; $category->slug = $this->slug; $imageName = Carbon::now()->timestamp.'.'.$this->image->extension(); $this->image->storeAs('categories',$imageName); $category->image = $imageName; $category->is_popular = $this->is_popular; $category->save(); session()->flash('message','Category has beeen created successfully!'); } public function render() { return view('livewire.admin.admin-add-category-component'); } }
-AdminAddProduct
<?php namespace App\Http\Livewire\Admin; use Livewire\Component; use Illuminate\Support\Str; use App\Models\Category; use App\Models\Product; use Livewire\WithFileUploads; use Carbon\Carbon; class AdminAddProductComponent extends Component { use WithFileUploads; public $name; public $slug; public $short_description; public $description; public $regular_price; public $sale_price; public $sku; public $stock_status = 'instock'; public $featured = 0; public $quantity; public $image; public $category_id; public function generateSlug() { $this->slug = Str::slug($this->name); } public function addProduct() { $this->validate([ 'name'=>'required', 'slug'=>'required', 'short_description' => 'required', 'description' => 'required', 'regular_price' => 'required', 'sale_price' => 'required', 'sku' => 'required', 'stock_status' => 'required', 'featured' => 'required', 'quantity' => 'required', 'image' => 'required', 'category_id' => 'required' ]); $product = new Product(); $product->name = $this->name; $product->slug = $this->slug; $product->short_description = $this->short_description; $product->description = $this->description; $product->regular_price = $this->regular_price; $product->sale_price = $this->sale_price; $product->SKU = $this->sku; $product->stock_status = $this->stock_status; $product->featured = $this->featured; $product->quantity = $this->quantity; $imageName = Carbon::now()->timestamp.'.'.$this->image->extension(); $this->image->storeAs('products',$imageName); $product->image = $imageName; $product->category_id = $this->category_id; $product->save(); session()->flash('message','Product has beeen added!'); } public function render() { $categories = Category::orderBy('name','ASC')->get(); return view('livewire.admin.admin-add-product-component',['categories'=>$categories]); } }
-AdminCategory
<?php namespace App\Http\Livewire\Admin; use Livewire\Component; use App\Models\Category; use Livewire\WithPagination; class AdminCategoriesComponent extends Component { public $category_id; use WithPagination; public function deleteCategory() { $category = Category::find($this->category_id); unlink('assets/imgs/categories/'.$category->newimage); $category->delete(); session()->flash('message','Category has beeen deleted successfully!'); } public function render() { $categories = Category::orderBy('name','ASC')->paginate(5); return view('livewire.admin.admin-categories-component',['categories'=>$categories]); } }
-AdminProduct
<?php namespace App\Http\Livewire\Admin; use Livewire\Component; use Livewire\WithPagination; use App\Models\Product; class AdminProductComponent extends Component { use WithPagination; public $product_id; public function deleteProduct() { $product = Product::find($this->product_id); unlink('assets/imgs/products/'.$product->image); $product->delete(); session()->flash('message','Product has beeen deleted!'); } public function render() { $products = Product::orderBy('created_at','DESC')->paginate(10); return view('livewire.admin.admin-product-component',['products'=>$products]); } }
-AdminAddHomeSlide
<?php namespace App\Http\Livewire\Admin; use Livewire\Component; use Carbon\Carbon; use App\Models\HomeSlider; use Livewire\WithFileUploads; class AdminAddHomeSlideComponent extends Component { use WithFileUploads; public $top_title; public $title; public $sub_title; public $offer; public $link; public $status; public $image; public function addSlide() { $this->validate([ 'top_title'=> 'required', 'title'=> 'required', 'sub_title'=> 'required', 'offer'=> 'required', 'link'=> 'required', 'status'=> 'required', 'image'=> 'required' ]); $slide = new HomeSlider(); $slide->top_title = $this->top_title; $slide->title = $this->title; $slide->sub_title = $this->sub_title; $slide->offer = $this->offer; $slide->link = $this->link; $slide->status = $this->status; $imageName = Carbon::now()->timestamp.'.'.$this->image->extension(); $this->image->storeAs('slider',$imageName); $slide->image = $imageName; $slide->save(); session()->flash('message','Slide has beeen added successfully!'); } public function render() { return view('livewire.admin.admin-add-home-slide-component'); } }
-AdminHomeSlide
<?php namespace App\Http\Livewire\Admin; use Livewire\Component; use App\Models\HomeSlider; class AdminHomeSliderComponent extends Component { public $slide_id; public function deleteSlide() { $slide = HomeSlider::find($this->slide_id); unlink('assets/imgs/slider/'.$slide->image); $slide->delete(); session()->flash('message','Slide has beeen deleted successfully!'); } public function render() { $slides = HomeSlider::orderBy('created_at','DESC')->get(); return view('livewire.admin.admin-home-slider-component',['slides'=>$slides]); } }
User
-Cart
<?php namespace App\Http\Livewire; use Livewire\Component; use Cart; class CartComponent extends Component { public function increaseQuantity($rowId) { $product = Cart::instance('cart')->get($rowId); $qty = $product->qty + 1; Cart::instance('cart')->update($rowId,$qty); $this->emitTo('cart-icon-component','refreshComponent'); } public function decreaseQuantity($rowId) { $product = Cart::instance('cart')->get($rowId); $qty = $product->qty - 1; Cart::instance('cart')->update($rowId,$qty); $this->emitTo('cart-icon-component','refreshComponent'); } public function destroy($id) { Cart::instance('cart')->remove($id); $this->emitTo('cart-icon-component','refreshComponent'); session()->flash('success_message','Item has been removed'); } public function clearAll() { Cart::instance('cart')->destroy(); $this->emitTo('cart-icon-component','refreshComponent'); } public function render() { return view('livewire.cart-component'); } }
-Category
<?php namespace App\Http\Livewire; use App\Models\Category; use App\Models\Product; use Livewire\Component; use Livewire\WithPagination; use Cart; class CategoryComponent extends Component { use WithPagination; public $pageSize = 12; public $orderBy = "Default Sorting"; public $slug; public function store($product_id,$product_name,$product_price) { Cart::add($product_id,$product_name,1,$product_price)->associate('\App\Models\Product'); session()->flash('success_message','Item added in Cart'); return redirect()->route('shop.cart'); } public function changePageSize($size) { $this->pageSize = $size; } public function changeOrderBy($order) { $this->orderBy = $order; } public function mount($slug) { $this->slug = $slug; } public function render() { $category = Category::where('slug',$this->slug)->first(); $category_id = $category->id; $category_name = $category->name; if($this->orderBy =='Price: Low to High') { $product = Product::where('category_id',$category_id)->orderBy('regular_price','ASC')->paginate($this->pageSize); } else if($this->orderBy =='Price: High to Low') { $product = Product::where('category_id',$category_id)->orderBy('regular_price','DESC')->paginate($this->pageSize); } else if($this->orderBy =='Sort by Newess') { $product = Product::where('category_id',$category_id)->orderBy('created_at','ASC')->paginate($this->pageSize); } else{ $product = Product::where('category_id',$category_id)->paginate($this->pageSize); } $categories = Category::orderBy('name','ASC')->get(); return view('livewire.category-component',['products'=>$product,'categories'=>$categories,'category_name'=>$category_name]); } }
-CheckOut
<?php namespace App\Http\Livewire; use Livewire\Component; class CheckoutComponent extends Component { public function render() { return view('livewire.checkout-component'); } }
-Details
<?php namespace App\Http\Livewire; use Livewire\Component; use App\Models\Product; use Cart; class DetailsComponent extends Component { public $slug; public function mount($slug) { $this->slug = $slug; } public function store($product_id,$product_name,$product_price) { Cart::add($product_id,$product_name,1,$product_price)->associate('\App\Models\Product'); session()->flash('success_message','Item added in Cart'); return redirect()->route('shop.cart'); } public function render() { $product = Product::where('slug',$this->slug)->first(); $rproducts = Product::where('category_id',$product->category_id)->inRandomOrder()->limit(4)->get(); $nproducts = Product::latest()->take(4)->get(); return view('livewire.details-component',['product'=>$product,'rproducts'=>$rproducts,'nproducts'=>$nproducts]); } }
-HeaderSearch
<?php namespace App\Http\Livewire; use Livewire\Component; class HeaderSearchComponent extends Component { public $q; public function mount() { $this->fill(request()->only('q')); } public function render() { return view('livewire.header-search-component'); } }
-Home
<?php namespace App\Http\Livewire; use Livewire\Component; use App\Models\HomeSlider; use App\Models\Product; use App\Models\Category; use Cart; class HomeComponent extends Component { public function store($product_id,$product_name,$product_price) { Cart::instance('cart')->add($product_id,$product_name,1,$product_price)->associate('\App\Models\Product'); session()->flash('success_message','Item added in Cart'); $this->emitTo('cart-icon-component','refreshComponent'); return redirect()->route('shop.cart'); } public function render() { $slides = HomeSlider::where('status',1)->get(); $lproducts = Product::orderBy('created_at','DESC')->get()->take(8); $fproducts = Product::where('featured',1)->inRandomOrder()->get()->take(8); $pcategories = Category::where('is_popular',1)->inRandomOrder()->get()->take(8); return view('livewire.home-component',['slides'=>$slides,'lproducts'=>$lproducts,'fproducts'=>$fproducts,'pcategories'=>$pcategories]); } }
-Search
<?php namespace App\Http\Livewire; use App\Models\Category; use App\Models\Product; use Livewire\Component; use Livewire\WithPagination; use Cart; class SearchComponent extends Component { use WithPagination; public $pageSize = 12; public $orderBy = "Default Sorting"; public $q; public $search_term; public function mount() { $this->fill(request()->only('q')); $this->search_term = '%'.$this-> q . '%'; } public function store($product_id,$product_name,$product_price) { Cart::instance('cart')->add($product_id,$product_name,1,$product_price)->associate('\App\Models\Product'); session()->flash('success_message','Item added in Cart'); $this->emitTo('cart-icon-component','refreshComponent'); return redirect()->route('shop.cart'); } public function changePageSize($size) { $this->pageSize = $size; } public function changeOrderBy($order) { $this->orderBy = $order; } public function render() { if($this->orderBy =='Price: Low to High') { $product = Product::where('name','like',$this->search_term)->orderBy('regular_price','ASC')->paginate($this->pageSize); } else if($this->orderBy =='Price: High to Low') { $product = Product::where('name','like',$this->search_term)->orderBy('regular_price','DESC')->paginate($this->pageSize); } else if($this->orderBy =='Sort by Newess') { $product = Product::where('name','like',$this->search_term)->orderBy('created_at','ASC')->paginate($this->pageSize); } else{ $product = Product::where('name','like',$this->search_term)->paginate($this->pageSize); } $categories = Category::orderBy('name','ASC')->get(); return view('livewire.search-component',['products'=>$product,'categories'=>$categories]); } }
-Shop
<?php namespace App\Http\Livewire; use App\Models\Category; use App\Models\Product; use Livewire\Component; use Livewire\WithPagination; use Cart; class ShopComponent extends Component { use WithPagination; public $pageSize = 12; public $orderBy = "Default Sorting"; public $min_value = 0; public $max_value = 1000; public function store($product_id,$product_name,$product_price) { Cart::instance('cart')->add($product_id,$product_name,1,$product_price)->associate('\App\Models\Product'); session()->flash('success_message','Item added in Cart'); $this->emitTo('cart-icon-component','refreshComponent'); return redirect()->route('shop.cart'); } public function changePageSize($size) { $this->pageSize = $size; } public function changeOrderBy($order) { $this->orderBy = $order; } public function addToWishlist($product_id, $product_name,$product_price) { Cart::instance('wishlist')->add($product_id,$product_name,1,$product_price)->associate('\App\Models\Product'); $this->emitTo('cart-icon-component','refreshComponent'); } public function removeFromWishlist($product_id) { foreach(Cart::instance('wishlist')->content() as $witem) { if($witem->id==$product_id) { Cart::instance('wishlist')->remove($witem->rowId); $this->emitTo('cart-icon-component','refreshComponent'); return; } } } public function render() { if($this->orderBy =='Price: Low to High') { $product = Product::whereBetween('regular_price',[$this->min_value,$this->max_value])->orderBy('regular_price','ASC')->paginate($this->pageSize); } else if($this->orderBy =='Price: High to Low') { $product = Product::whereBetween('regular_price',[$this->min_value,$this->max_value])->orderBy('regular_price','DESC')->paginate($this->pageSize); } else if($this->orderBy =='Sort by Newess') { $product = Product::whereBetween('regular_price',[$this->min_value,$this->max_value])->orderBy('created_at','ASC')->paginate($this->pageSize); } else{ $product = Product::whereBetween('regular_price',[$this->min_value,$this->max_value])->paginate($this->pageSize); } $categories = Category::orderBy('name','ASC')->get(); return view('livewire.shop-component',['products'=>$product,'categories'=>$categories]); } }
6.View
Nhóm: Nguyễn Tất Hào
LinkGithub: https://github.com/NguyenTatHao/Surfside-Media