Laravel 入门

445 阅读6分钟

开发环境 Windows 篇

下载地址 Download | Laragon - portable, isolated, fast & powerful universal development environment for PHP, Node.js, Python, Java, Go, Ruby.

windows的同学们有福了,你无需任何繁琐的配置都可以很方便的开发 laravel,这就是laragon。用 laragon 做起 laravel 开发,真的无比方便好用

它里面自带composerphpmysqlredis memcached,还包含有cmder这个非常好用的命令行。

使用中文

点右键,preferences,现在可以把language设置成chinese了,其他的一切都保持默认就好。

image.png

Tips: 如果你的 apache 启动报错,可尝试将右键参数里的ssl端口设置为其他端口,例如3000

开始来跑 laravel 项目了

右键快速创建,选laravel,输入项目名称meetup

等命令跑完后,laragon会自动配置后缀dev的虚拟机,打开chrome浏览器,输入

image.png

看到这个画面就说明,你已经成功的跑起 laravel 了

1. The Laravel Way

image.png

laravel 的根本骨架是上面的 mvc 结构,不过这一集只来关注 route -> controller -> view 的这条线。

到 routes/web.php 中修改

<?php

use Illuminate\Support\Facades\Route;
// 引入控制器文件
use App\Http\Controllers\WelcomeController;

Route::get('/',[WelcomeController::class,'index']);
Route::get('about', [WelcomeController::class,'about']);

添加 controller

php artisan make:controller WelcomeController

image.png

image.png 启动项目 php artisan serve

views

  1. 将提供的assets文件夹,拷贝到meetup/public目录。
  2. resources/views目录中新建一个叫做welcome文件夹。
  3. index.htmlabout.html拷贝到resources/views/welcome,并把文件后缀名都改为blade.php

image.png

image.png

1. controller 里传数据

public function index()
{
    $issues = [
        ['title' => 'PHP lovers'],
        ['title' => 'Rails and Laravel']
    ];
    return view('welcome.index')->with('issues', $issues);
}

删除一条重复的 issue,然后给另一条做 foreach 循环

@foreach($issues as $issue)
    <li class="...">
        ...
        <a href="issues_show.html">{{$issue['title']}}</a>
        ...
    </li>
@endforeach

Sub-Views

分离为一个 子视图文件

image.png

  1. 在 resources/views/welcome 中添加活动列表 _issue_list.blade.php文件。
  2. index.blade.php活动列表部分的代码,剪切到_issue_list.blade.php中。
  3. 再到 index.blade.php中加上
@include('welcome._issue_list')

image.png

数据库

据库连接

这次相关的配置文件是根目录下的 .env 文件,里面写明了要使用的数据库和用到的账号密码。

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=meetup
DB_USERNAME=root
DB_PASSWORD=root

建立数据表结构

更改数据库的表结构,laravel 给出的方法是 migration laravel.com/docs/5.5/mi…

php artisan make:migration create_issues_table --create=issues

生成的文件名的前面是时间戳,2017_11_29… 今天就是 2017年11月29号。里面可以添加需要的字段。

至于database/migrations文件夹中已经默认存在的create_users_tablecreate_password_resets_table是我们后面做用户登陆注册需要用到的,咱们暂时先不需要关注它。

public function up()
{
    Schema::create('issues', function (Blueprint $table) {
        $table->increments('id');
        $table->string('title');
        $table->timestamps();
    });
}

image.png

运行

php artisan migrate

来把内容真正写进 mysql 数据库。

建立 model

model 文件要放在 app/Models 下面,名字叫 Issue.php

php artisan make:model Models/Issue
class Issue extends Model
{
}

这里的 class 命名是很关键的,如果数据库中的表名是issues,那这里的class名就必须是 Issue,也就是首字母大写,同时变成单数。为啥要这样?因为这样laravel就可以建立自动的 class 到 table 的映射关系了,以后要操作issues这张表,就无比的方便。

Tips:如果你需要将model和对应的migration一下全部建出来,下次也可以直接使用php artisan make:model Models/Issue -m 这一条命令。这样他会同时生成modelmigration

这样就可以打开 laravel tinker 来真正对这样表进行操作了,具体可以参考 laravel.com/docs/5.5/ar…

php artisan tinker

插入需要的记录

use App\Models\Issue

Issue::create(['title' => 'PHP Lover'])
Issue::create(['title' => 'Rails and Laravel'])
Issue::all()

屏幕提示一个错误信息 Illuminate\Database\Eloquent\MassAssignmentException with message 'title'

这个是 laravel 为了防止坏人恶意提交数据攻击网站,而采用的自我保护机制。你想啊,如果不加说明,坏蛋们就可以在表单中人为植入其他的参数。

要做的修改非常简单,就是到 Issue模型中,添加白名单

class Issue extends Model
{
    protected $fillable = ['title'];
}

image.png

image.png

修改 controller 和 view

现在在 controller 中读取数据

use App\Models\Issue;

class WelcomeController extends Controller
{
    public function index()
    {
        $issues = Issue::orderBy('created_at', 'desc')
            ->take(2)
            ->get();

        return view('welcome.index')->with('issues', $issues);
    }
}

Tips:

  1. orderBy的意思是排序。created_at是添加数据的时候,laravel自动帮添加的当前时间。
  2. desc是倒序(从大到小)。这样最新发布的issue会就在最上面了。
  3. take(2)的意思,是说这里只读取两条数据。

再到 _issue_list.blade.php 中在稍作修改就好了。

{{$issue->title}}

CURD

现在有了 issue 也就是活动信息这个东东,后面就开始对他 Create Update Read Delete 了。

这一集只是开始,瞄准 issue的展示和删除。关于 CURD 比较详细的解释,参考 laravel.com/docs/5.5/el…

往表添加字段

添加 content 到 issue

首先运行一下 migration

php artisan make:migration add_content_to_issues_table --table=issues

image.png

class AddContentToIssuesTable extends Migration
{
    public function up()
    {
        Schema::table('issues', function (Blueprint $table) {
            $table->text('content');
        });
    }

    public function down()
    {
        Schema::table('issues', function (Blueprint $table) {
            $table->dropColumn('content');
        });
    }
}

别忘了php artisan migrate,来跑一下迁移命令。

Tips: down里面写的是 up的反操作。将来如果写错了,将来可以通过php artisan migrate:rollback这一条命令来回滚对数据库的操作。

image.png

插入一下数据

下面打开 php artisan tinker

use App\Models\Issue
$i = Issue::find(1)
$i->content = "The PHP Framework For Web Artisans"
$i->save()

$i = Issue::find(2)
$i->content = "Imagine what you could build if you learned Ruby on Rails ..."
$i->save()

image.png

添加路由

web.php中加上

Route::get('issues/{issue}', [IssuesController::class,'show']);

新建控制器

<?php

namespace App\Http\Controllers;

use App\Models\Issue;
use Illuminate\Http\Request;

class IssuesController extends Controller
{
   public function show($id)
   {
       $issue = Issue::find($id); // 查询数据
      // return $issue;
       return view('issues.show')->with('issue',$issue);
   }
}

使用命名路由

// 使用命名路由
Route::get('issues/{issue}', [IssuesController::class,'show'])->name('issues.show');

view

<a href="{{route('issues.show',$issue->id)}}">{{$issue->title}}</a>

删除数据

public function destroy($id)
{
    Issue::destroy($id);
    return redirect('/');
    
}

新增数据

Route::post('add', [IssuesController::class,'store'])->name('issues.store');

view

<form class="am-form" action="{{route('issues.store')}}" method="post">
    {{csrf_field()}}
    <fieldset>
        <div class="am-form-group">
            <label>标题</label>
            <input type="text" placeholder="输入活动标题" name="title">
        </div>

        <div class="am-form-group">
            <label>内容</label>
            <textarea rows="5" name="content"></textarea>
        </div>

        <button type="submit" class="am-btn am-btn-default">提交</button>
    </fieldset>
</form>

将数据添加到数据库中

public function store(Request $request)
{
    Issue::create($request->all());
    return redirect('/');
}

Issue.php模型中,加上content白名单

protected $fillable = ['title', 'content'];

分页

public function index()
{
   // $issues = Issue::orderBy('created_at','desc')->get();
    $issues = Issue::orderBy('created_at', 'desc')->paginate(5);
    return view('issues.index')->with('issues',$issues);

}

1.修改controller,将之前查询用的get(),改为paginate(5)。 这里5的意思是说,每页显示5条数据。

issues/index.blade.php中,用{{ $issues->links() }},代替之前写死的分页代码。

1. 修改

view

<div class="am-container">
    <div class="header">
        <div class="am-g">
            <h1>修改活动</h1>
        </div>
        <hr>
    </div>

    <form class="am-form" action="{{route('issues.update',$issue->id)}}" method="post">
        {{ csrf_field() }}
        {{ method_field('PUT') }}
        <fieldset>
            <div class="am-form-group">
                <label>标题</label>
                <input type="text" placeholder="输入活动标题" name="title" value="{{$issue->title}}">
            </div>

            <div class="am-form-group">
                <label>内容</label>
                <textarea rows="5" name="content" >{{$issue->content}}</textarea>
            </div>

            <button type="submit" class="am-btn am-btn-default">提交</button>
        </fieldset>
    </form>
</div>

因为这里使用了put动词,而form表单并不能发起putpatchdelete请求。 laravel的解决方式是,添加 {{ method_field('PUT') }},来伪造一个put请求。

Route::put('update/{issue}', [IssuesController::class,'update'])->name('issues.update');
// 修改
public function update(Request $request,$id)
{
    $issue = Issue::find($id);
    $issue->update($request->all());
    return redirect(route('issues.show',$id));
    //return view('issues.show')->with('issue',$issue);

}

一对多 这也就是我们数据库关联中所说的一对多关系。 做评论功能,关注的核心问题也是has many关系。一个issue,对应多个comment

1. 创建 model 和 migration

php artisan make:model Models/Comment -m

修改 migration 文件

public function up()
{
    Schema::create('comments', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('issue_id');
        $table->string('name');
        $table->string('email');
        $table->text('content');
        $table->timestamps();
    });
}

运行migrate,生成comments表。

php artisan migrate

Tips: 为了体现归属关系,也就是一个 issue 对应多个 comment 的关系。要在comments中添加一个新的字段issue_id,这个在后面会有妙用。

view

<form class="am-form" method="post" action="{{route('comments.store')}}">
    {{csrf_field()}}
    <input type="hidden" name="issue_id" value="{{$issue->id}}">
    ...
</form>

Tips: 因为comments表中,有一个issue_id。这里就需要放一个隐藏的input,将当前issueid传递过去。

Route::post('comments', [CommentsController::class,'store'])->name('comments.store');
class CommentsController extends Controller
{
    public function store(Request $request)
    {
        return $request->all();
    }
}

image.png

当前发布评论所在页面的issue_id已经传递过来了

进一步修改为

//一定记得要use model
use App\Models\Comment;

class CommentsController extends Controller
{
    public function store(Request $request)
    {
        Comment::create($request->all());
        return back();
    }
}

image.png

建立一对多的关系

第一步,到Issue.php模型中添加

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Issue extends Model
{
    protected $fillable = ['title', 'content'];

    public function comments()
    {
        return $this->hasMany('App\Models\Comment');
    }
}

Tips: 一个issue有很多个comment,所以这里要用复数的comments

第二步,到Comment.php模型 中,添加

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    protected $fillable = ['issue_id', 'name', 'email', 'content'];
    public function issue()
    {
        return $this->belongsTo('App\Models\Issue');
    }
}

Tips: 每个comment只都属于一个issue,所以这里的issue是单数。

好了,就是这简单的两步。issuecomment的关联关系就已经定义完毕了。 再来刷新一下页面,comments出现了。

最后一步,完成show方法

public function show($id)
{
    $issue = Issue::find($id);
    $comments = $issue->comments;
    return view('issues.show', compact('issue', 'comments'));
}

Tips: 如果需要发送多个数据到模板,可以使用phpcompact函数来简化代码。

view

@foreach($comments as $comment)
    <li class="am-comment">
        <img src="/assets/img/avatar2.png" alt="" class="am-comment-avatar" width="48" height="48">

        <div class="am-comment-main">
            <header class="am-comment-hd">
                <div class="am-comment-meta">
                    <span class="am-comment-author">{{$comment->name}}</span>
                    {{$comment->created_at->diffForHumans()}}
                </div>
            </header>
            <div class="am-comment-bd">{{$comment->content}}</div>
        </div>
    </li>
@endforeach

laravel 常用命令

laravel 的命令有挺多的,不过常用的也就是那么几条,我们这次课程也都基本用到了。

常用的命令和意义我都列出来了,如果记不住,可以将下面的表格截图保存一份,照着多打个几次,慢慢就都记住了。

命令意义
php artisan make:controller IssuesController新建控制器
php artisan make:controller IssuesController -r新建控制器并自动生成对应 RESTful 风格路由相关 CURD 方法
php artisan make:migration create_issues_table --create=issues新建一个迁移文件
php artisan migrate运行迁移,将迁移文件的内容写入数据库中
php artisan migrate:rollback回滚上一次迁移的内容
php artisan make:model Models/Issue新建模型
php artisan make:model Models/Issue -m同时生成模型和对应的迁移文件
php artisan tinker使用命令行来调试 Laravel、操作模型等
php artisan route:list打印出所有的路由

控制器

作用就是整个程序的逻辑部分,可以调用模型做一些数据处理,还可以显示模板,发送数据到模板,跳转等等。 如果需要发送多个数据到模板,那么可以使用compact函数来简化代码。

模型

模型一般都放在App\Models中。初学模型,暂时也不用太深入,知道我们是使用模型来操作数据库的就好。目前需要掌握的也只有定义 白名单 和关联。我们实现了issuescomments一对多关联,comments表中一定要有issue_id来做对应。模型的关联除了咱们这次课用到的一对多外,还有一对一多对多,这些我会在后续的课程再跟大家介绍。

这里还是用 php artisan tinker来看一下这两个例子。

迁移

数据库中,有一个migrations表,这里记录的是所有已经跑过的迁移列表,如果这里已经有记录了,那你再跑php artisan migrate,他是不会重复执行的。换句话说,就是只会运行之前从没有跑过的迁移文件。

如果你觉得迁移用的不习惯,觉得直接动手使用sequal pro或者navicat这类软件建表更方便,那其实也完全没有任何问题的,laravel一样可以用的好好的。

布局模板

再来看模板部分,将页面相同的公共部分,抽离出来,放在layout/app.blade.php,并且加上@yield('content')

其他的页面

@extends('layouts.app')
@section('content')
    <!-- 不同的部分 -->
@endsection

参数

路由中的issues.show的对应的uri包含了这样一个有大括号的路径,/issues/{issue},那么超链接那里就可以写{{route('issues.show', 10)}},这个10,就会替换掉{issue},生成的路径也就是/issues/10

除此外,在控制器的方法中,直接可以就可以使用这个$id,也就是传过来的10,而不用像其他框架那样要用$_GET['id']来取值了。

function show($id) {
    return $id;
}

api.php

routes文件夹中,除了web.php外,还有个api.php,这个是做接口使用的。区别是会去掉session cookie等相关中间件,路径上会自动添加/api前缀。这个目前也暂时不需要了解太多。后面的项目我们碰到做接口再细说了。