到目前为止,我们一直在建立电子商务网站的管理方面,这是魔术发生的后端方面。
现在,网站所有者能够添加和管理产品,让我们可以出售和赚钱。在这篇文章中,我们将为网站创建主页。
对于前端,我们有选项,我一直在后端使用bootstrap,并将继续在此帖子上使用它,因为良好的组件不是
因此,基本上我们想展示各种各样的英雄,可能随机产品显示了案例,有关该网站的一些信息,特色产品的旋转木马,一种特色产品,现在可以购买的能力,现在几乎是您期望看到的基本东西在主页上。
请注意,如果您转到我们网站的索引,您将获得404,让我们注册一个控制器并显示主页。
首先生成控制器
php artisan make:contoller HomeController --test
接下来,生成我们将使用此控制器使用的视图
php artisan make:view layouts.home
php artisan make:view home.index -e layouts.home
接下来,我们为我们的房屋注册路线。我们将使用一个单独的路由组组,因为我们将与管理组不同。在routes/web.php
文件中,在管理路由组下方添加以下片段
use App\Http\Controllers\HomeController as FrontendHomeController;
Route::group([], static function () {
Route::get('/', FrontendHomeController::class, 'index')->name('home.index');
});
现在转到App\Http\Controllers\HomeController
并定义索引动作
/**
* Display the application home page.
*
* @return Renderable
*/
public function index(): Renderable
{
return view('home.index');
}
现在,如果您访问索引URL,您只会看到一个空白页
布局
对于我们的主题,我们将使用基于Bootstrap 4的 Bootstrap电子商务主题的早期版本,它不再在网站上使用,它是开源的,已关闭,最近由MDB拥有。
无论如何,我仍然有一个在zip文件中压缩的样式表的副本,您可以从this link
下载下载文件并在您的resources/sass
文件夹中提取内容
创建另一个文件_store-variables.scss
,以便我们可以自定义bootstrap,或者只需从this link
现在,我们的样式是让我们添加一些JavaScript。创建一个文件resources/js/store.js
和以下代码以注册涡轮和刺激
import './bootstrap';
import './elements/turbo-echo-stream-tag';
import './libs/turbo';
import './libs';
最后,让我们告诉Vite来编译我们新创建的文件。打开vite.config.js
添加以下片段为输入数组,如以下
export default defineConfig({
plugins: [
laravel({
input: [
'resources/sass/app.scss',
'resources/js/app.js',
'resources/js/store.js',
'resources/sass/store.scss',
],
refresh: true,
}),
],
});
现在,我们的设置已经完成,让我们创建布局。打开layouts.home
视图并添加以下HTML摘要
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="csrf-token" content="{{ csrf_token() }}" />
<title>@yield('title') | {{ config('app.name') }}</title>
@vite(['resources/sass/store.scss', 'resources/js/store.js'])
</head>
<body>
<h1>Hello world</h1>
</body>
保存文件并访问主页以确认是否正确设置了样式。
我是,所以现在让我们添加主页上的实际布局,用
替换我们的“ Hello World”
<header class="section-header border-bottom">
<section class="header-main border-bottom">
<div class="container">
<div class="row d-flex align-items-center justify-content-between">
<div class="col-2 navbar-light d-md-none">
<button class="navbar-toggler border-0" type="button" data-toggle="collapse"
data-target="#main_nav" aria-controls="main_nav" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon text-dark"></span>
</button>
</div>
<div class="col-md-3 col-4">
<div class="brand-wrap mx-auto">
<a href="{{ route('home.index') }}">
<img class="logo" src="{{ asset('images/JPEG/Apple Plug Logo 1.png') }}">
</a>
</div> <!-- brand-wrap.// -->
</div>
<div class="col-lg-6 col-sm-12 col-5 d-none d-md-block">
<form action="#" class="search" {{ stimulus_controller('form-search') }}>
<div class="input-group w-100">
<input type="text" class="form-control" placeholder="Search" {{ stimulus_target('form-search', 'input') }} {{ stimulus_action('form-search', 'searchByEnter', 'keypress') }}>
<div class="input-group-append">
<button class="btn btn-primary" type="button" {{ stimulus_action('form-search', 'search') }}>
<i class="fa fa-search"></i>
</button>
</div>
</div>
</form>
</div> <!-- col.// -->
<div class="col-md-2 col-4">
<div class="widgets-wrap float-right ml-auto mt-0">
<div class="widget-header mr-3 ">
<a href="#" class="icon icon-sm rounded-circle border">
<i class="fas fa-shopping-cart"></i>
</a>
<span class="badge badge-pill badge-danger notify">1</span>
</div>
</div> <!-- widgets-wrap.// -->
</div> <!-- col.// -->
</div> <!-- row.// -->
</div> <!-- container.// -->
</section> <!-- header-main .// -->
<nav class="navbar navbar-main navbar-expand-lg navbar-light mb-0 py-0 pb-y-2 mb-md-2">
<div class="container">
<div class="collapse navbar-collapse" id="main_nav">
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link" href="{{ route('home.index') }}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Shop</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Contact</a>
</li>
@foreach ($categories->take(4) as $category)
<li class="nav-item">
<a class="nav-link"
href="#">{{ $category->name }}
</a>
</li>
@endforeach
<li class="nav-item d-md-none">
<div class="col-12 py-5">
<form action="#" class="search" {{ stimulus_controller('form-search') }}>
<div class="input-group w-100">
<input type="text" class="form-control" placeholder="Search" {{ stimulus_target('form-search', 'input') }} {{ stimulus_action('form-search', 'searchByEnter', 'keypress') }}>
<div class="input-group-append">
<button class="btn btn-primary" type="button" {{ stimulus_action('form-search', 'search') }}>
<i class="fa fa-search"></i>
</button>
</div>
</div>
</form>
</div>
</li>
</ul>
</div>
</div>
</nav>
</header>
将徽标图像更改为您自己的徽标,或者只需从this link
下载此徽标请注意,在Navbar上显示类别的类别循环,我们无法在每个控制器上包含分类变量,但是我们将使用view composer,
实际上,我们将通过此教程系列使用其中的许多。
首先在app/View/Composers
中创建一个名为Categories.php
的文件,然后粘贴以下代码
namespace App\View\Composers;
use App\Models\Category;
use Illuminate\View\View;
class Categories
{
/**
* Bind data to the view.
*
* @param View $view
* @return void
*/
public function compose(View $view): void
{
$categories = Category::with('children')
->whereNull('parent_id')
->withCount('products')
->get()
->filter(fn($category) => $category->products_count > 0);
$view->with('categories', $categories);
}
}
视图作曲家允许我们将一块数据绑定到特定的数据,因此每次呈现视图时都包括在内。在我们的情况下,我们查询所有类别,并在其中筛选出没有产品的那些类别,因为,这是什么。
为了使作曲家实际工作,我们需要在服务提供商中注册它。让我们为此创建一个提供商
php artisan make:provider ViewServiceProvider
创建提供商后,将其注册到config/app.php
现在打开提供商并在boot
方法中注册作曲家
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
View::composer('layouts.home', Categories::class);
}
现在,如果您重新访问索引页面,则应该看到带有NAV和搜索的不错的标头
接下来,让我们添加页脚产生内容。在关闭标题标签之后粘贴以下片段
<footer class="section-footer border-top padding-y">
<div class="container">
<section class="footer-top padding-y">
<div class="row">
<aside class="col-md">
<h6 class="title">Company</h6>
<ul class="list-unstyled">
<li> <a href="#">Home</a></li>
<li> <a href="#">About us</a></li>
<li> <a href="#">Shop</a></li>
<li> <a href="#">Contact us</a></li>
</ul>
</aside>
<aside class="col-md">
<h6 class="title">Legal Stuff</h6>
<ul class="list-unstyled">
<li> <a href="#">Refund Policy</a></li>
<li> <a href="#">Terms of Service</a></li>
<li> <a href="#">Privacy Policy</a></li>
<li> <a href="#">Open dispute</a></li>
</ul>
</aside>
<aside class="col-md">
<h6 class="title">Account</h6>
<ul class="list-unstyled">
<li> <a href="#"> User Login </a></li>
<li> <a href="{{ route('register') }}"> User register </a></li>
<li> <a href="#"> My Account </a></li>
<li> <a href="#"> My Orders </a></li>
</ul>
</aside>
<aside class="col-md">
<h6 class="title">Social</h6>
<ul class="list-unstyled">
<li><a href="#"> <i class="fab fa-facebook"></i>
Facebook </a></li>
<li><a href="#"> <i class="fab fa-twitter"></i>
Twitter </a></li>
<li><a href="#"> <i class="fab fa-instagram"></i>
Instagram </a></li>
<li><a href="#"> <i class="fab fa-whatsapp"></i>
WhatsApp </a></li>
</ul>
</aside>
</div> <!-- row.// -->
</section> <!-- footer-top.// -->
<section class="footer-bottom border-top row">
<div class="col-md-2">
<p class="text-muted"> © {{ date('Y') }} {{ config('settings.general.legal_name') }} </p>
</div>
<div class="col-md-8 text-md-center">
<span class="px-2">{{ config('settings.general.contact_email') }}</span>
<span class="px-2">{{ config('settings.general.phone') }}</span>
</div>
<div class="col-md-2 text-md-right text-muted">
<i class="fab fa-lg fa-cc-visa"></i>
<i class="fab fa-lg fa-cc-paypal"></i>
<i class="fab fa-lg fa-cc-mastercard"></i>
</div>
</section>
</div>
</footer>
如果保存和刷新页面,则应看起来像下面的图像
现在让我们转到此帖子的实际内容
英雄
每个电子商务上方都上方的英雄事物,这就是我们将在下面实施的,打开home.index
视图并粘贴以下片段
<section class="section-intro bg-primary p-0">
<div id="carousel1_indicator" class="carousel slide" data-ride="carousel" style="height: 80vh">
<ol class="carousel-indicators">
<li data-target="#carousel1_indicator" data-slide-to="0" class="active"></li>
</ol>
<div class="carousel-inner">
<div class="carousel-item active" style="height: 80vh">
<div class="d-block w-100 h-100 text-white "
style="background-size: cover; background-repeat: no-repeat; background-position: center; background-image: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.6)), url('{{ asset('images/banners/7.jpg') }}')">
<div class="container h-100">
<div class="d-flex h-100 align-items-center">
<div class="col-md-7 text-center text-md-start">
<h1 class="text-capitalize display-4" style="font-weight: 500">
quality guaranteed products at reasonable pricing
</h1>
<p class="lead">
We bring you amazing products of your choice backed by exceptional after care service.
</p>
<a href="#" class="btn btn-primary">
Shop Now
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
从此链接tot下载横幅文件夹获取背景图像,或者您可以在Unsplash上找到另一个。
添加以下片段以显示“为什么要从我们那里购买”部分
<section class="section-specials padding-y border-bottom">
<div class="container">
<div class="row">
<div class="col-md-4 mb-4 mb-md-0">
<figure class="itemside">
<div class="aside">
<span class="icon-sm rounded-circle bg-primary">
<i class="fa fa-money-bill-alt white"></i>
</span>
</div>
<figcaption class="info">
<h6 class="title">Reasonable prices</h6>
<p class="text-muted">We bring you amazing products of your choice backed by exceptional after care service.</p>
</figcaption>
</figure> <!-- iconbox // -->
</div><!-- col // -->
<div class="col-md-4 mb-4 mb-md-0">
<figure class="itemside">
<div class="aside">
<span class="icon-sm rounded-circle bg-danger">
<i class="fa fa-comment-dots white"></i>
</span>
</div>
<figcaption class="info">
<h6 class="title">Customer support 24/7 </h6>
<p class="text-muted">Ring us anytime to get assistance on anything that might be bothering you when using any of our products </p>
</figcaption>
</figure> <!-- iconbox // -->
</div><!-- col // -->
<div class="col-md-4 mb-4 mb-md-0">
<figure class="itemside">
<div class="aside">
<span class="icon-sm rounded-circle bg-success">
<i class="fa fa-truck white"></i>
</span>
</div>
<figcaption class="info">
<h6 class="title">Quick delivery</h6>
<p class="text-muted">
Enjoy the convinience of quick deliveries as soon as you place an order with us
</p>
</figcaption>
</figure> <!-- iconbox // -->
</div><!-- col // -->
</div> <!-- row.// -->
</div> <!-- container.// -->
</section>
现在,让我们随机选择一些类别以在您的主页上显示,知道促进销售等等
<section class="padding-y bg-light">
<div class="container">
<!-- ============ COMPONENT BS CARD WITH IMG ============ -->
<div class="row gy-3">
@foreach ($categories->take(3) as $category)
<div class="col-lg-4 col-md-6 mb-4 mb-md-0">
<article class="card bg-dark border-0">
<img src="{{ $category->products->random()->fetchAllMedia()->random()?->file_url }}"
class="card-img opacity" style="object-fit: cover" height="200">
<div class="card-img-overlay">
<h5 class="mb-0 text-white">{{ $category->name }}</h5>
<p class="card-text text-white">{{ Str::limit($category->description) }}</p>
<a href="#"
class="btn btn-secondary">
Discover
</a>
</div>
</article>
</div>
@endforeach
</div>
</div>
</section>
为了包括类别变量,让我们注册一个视图作曲家到此视图。打开服务提供商并将此代码添加到启动方法
View::composer('home.index', Categories::class);
现在,上面的刀片片段有一个小问题,当我们在随机产品上称为fetchAllMedia()
时,有时该产品可能没有图像,这可能会导致某些不必要的副作用
我们将覆盖fetchAllMedia()
方法以返回默认图像时,当未添加图像中。
fetchAllMedia
从cloudinary-laravel
软件包中返回Media
型号的集合,因此我们将创建一个自己的一个,以便在没有找到的时候返回
创建一个名为App\Null\MediaAlly\DefaultMedia
的类,并像这样定义
<?php
namespace App\Null\MediaAlly;
use Illuminate\Support\Facades\File;
use SplFileInfo;
class DefaultMedia
{
public string $file_url;
public int|false $size;
public string $file_name;
public string|false $file_type;
public function __construct()
{
$file = new SplFileInfo(public_path('images/null/default.png'), );
$this->file_url = asset('images/null/default.png');
$this->file_type = $file->getType();
$this->file_name = $file->getFilename();
$this->size = $file->getSize();
}
}
因此,此类从给定路径创建一个null映像的splfileinfo对象,请确保您的未空,您可以从此链接下载null映像
现在打开产品模型,然后用
替换use MediaAlly
use MediaAlly {
MediaAlly::fetchAllMedia as fetchMedia;
}
这是这样我们可以安全覆盖fetchAllMedia()
现在添加以下方法覆盖此方法
/**
* Fetch all media that belong to this product
*
* @return Collection
*/
public function media(): Collection
{
$media = $this->fetchMedia();
if ($media->count() < 1) {
return collect([new DefaultMedia]);
}
return $media;
}
/**
* @see Product::media()
*
* @return Collection
*/
public function fetchAllMedia(): Collection
{
return $this->media();
}
如果产品没有任何图像,我们返回默认图像的集合,否则我们返回图像,这是一个穷人的实现,null对象模式
当我在本教程的一半时,我意识到本主页终于在制作了其他页面后终于创建了,因此我们可以在不使用占位符的情况下正确链接。
话虽如此,我们将结束本教程,并继续在下一篇文章中构建产品索引页面,以允许客户商店
与此同时,请确保您在DEV上关注我,以便您可以在新帖子开始时获得Ping。
快乐编码!