Laravel电子商务教程:第11部分,主页
#网络开发人员 #php #laravel

到目前为止,我们一直在建立电子商务网站的管理方面,这是魔术发生的后端方面。

现在,网站所有者能够添加和管理产品,让我们可以出售和赚钱。在这篇文章中,我们将为网站创建主页。

对于前端,我们有选项,我一直在后端使用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"> &copy {{ 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>

如果保存和刷新页面,则应看起来像下面的图像

ecommece layout without content

现在让我们转到此帖子的实际内容

英雄

每个电子商务上方都上方的英雄事物,这就是我们将在下面实施的,打开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()方法以返回默认图像时,当未添加图像中。

fetchAllMediacloudinary-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。

快乐编码!