介绍

这段时间在写一个考试系统ChemLab,期间用到了AJAX实现增删查改(CRUD)页面,现在写一个Laravel入门教程吧,一步步实现最基本的CRUD页面。

项目地址,求StarLaravel_AJAX_CRUD

演示地址:http://115.159.147.250/task

先来看看效果图:
http://7xibui.com1.z0.glb.clouddn.com/crud.gif

这个教程需要安装homestead环境,关于homestead可以参考我的另一篇博文:windows下安装Homestead开发环境

创建项目

vagrant@homestead:~/Code$ laravel new AJAX_CRUD
vagrant@homestead:~/Code$ cd AJAX_CRUD/

修改配置

修改数据库

打开.env,主要是修改数据库方面的,我们选择task数据库,如下:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=task
DB_USERNAME=homestead
DB_PASSWORD=secret

然后在数据库中创建task数据库。

修改网址映射

注意这部分是在主机下进行的,不是在虚拟机homestead中进行的,后面我提到主机应该注意。
修改homestead的配置文件:~/.homestead/Homestead.yaml,增加如下内容:

sites:
    - map: crud.app
      to: /home/vagrant/Code/AJAX_CRUD/public

修改C:\Windows\System32\drivers\etc\hosts,增加一条记录:

192.168.10.10  crud.app

重启homestead虚拟机:

homestead reload --provision

访问项目

浏览器访问crud.app,应该能见到如下页面:
http://7xibui.com1.z0.glb.clouddn.com/QQ%E6%88%AA%E5%9B%BE20160817111329.png

引入Bootstrap、JQuery、Toastr

刚创建的项目什么都没有,在resources/welcome.blade.php中加入bootstrapJQueryToastr

<title>Task Manager</title>

<link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="http://cdn.bootcss.com/toastr.js/2.1.3/toastr.min.css" rel="stylesheet">
<script src="http://cdn.bootcss.com/jquery/3.1.0/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="http://cdn.bootcss.com/toastr.js/2.1.3/toastr.min.js"></script>

将如下内容删除:

<style>
    html, body {
        height: 100%;
    }

    body {
        margin: 0;
        padding: 0;
        width: 100%;
        display: table;
        font-weight: 100;
        font-family: 'Lato', sans-serif;
    }

    .container {
        text-align: center;
        display: table-cell;
        vertical-align: middle;
    }

    .content {
        text-align: center;
        display: inline-block;
    }

    .title {
        font-size: 96px;
    }
</style>
...
<div class="content">
    <div class="title">Laravel 5</div>
</div>

创建Eloquent模型、数据表

vagrant@homestead:~/Code/AJAX_CRUD$ php artisan make:model Task -m
Model created successfully.
Created Migration: 2016_08_17_033029_create_tasks_table

编辑模型:app/Task.php,如下:

class Task extends Model
{
    protected $fillable = ['name', 'content'];
}

编辑database/migrations/2016_08_17_033029_create_tasks_table.php,如下

...
public function up()
{
    Schema::create('tasks', function (Blueprint $table) {
        $table->increments('id');
        $table->text('name');
        $table->text('content');
        $table->timestamps();
    });

    App\Task::create([
        'name' => '任务1',
        'content' => '完成crud'
    ]);
    App\Task::create([
        'name' => '任务1',
        'content' => '完成教程'
    ]);
}
...

创建数据表:

vagrant@homestead:~/Code/AJAX_CRUD$ php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2016_08_17_033029_create_tasks_table

到数据库中查看,你会发现task数据库中多了一些表和记录。

构建页面

创建控制器

vagrant@homestead:~/Code/AJAX_CRUD$ php artisan make:controller TaskController
Controller created successfully.

修改路由

编辑app/Http/routes.php

Route::get('/', function () {
    return redirect('/task');
});

Route::resource('/task', 'TaskController');

这样所有动作都由我们刚刚创建的控制器来执行了。

这里的Route::resource方法可以看这个:RESTful Resource Controllers,它默认帮你创建这几个http verb

  • GET
  • POST
  • PUT
  • DELETE

这样就不用自己写路由了。

修改控制器

编辑app/Http/Controllers/TaskController.php,在类中增加如下方法:

use App\Task;
...
public function index() {
    return view('welcome', [
        'tasks' => Task::all()
    ]);
}

编辑页面

编辑resources/welcome.blade.php,如下

<div class="container">
    {{--task list--}}
    <div class="panel panel-default">
        <div class="panel-heading">
            任务管理器
        </div>
        <div class="panel-body">
            <button class="btn btn-primary" id="add">添加任务</button>
        </div>
        <table class="table table-striped">
            <thead>
            <tr>
                <th>id</th>
                <th>Name</th>
                <th>Content</th>
                <th>Created_at</th>
                <th>Action</th>
            </tr>
            </thead>
            <tbody id="task-list">
            @foreach($tasks as $task)
                <tr id="task{{ $task->id }}">
                    <td>{{ $task->id }}</td>
                    <td>{{ $task->name }}</td>
                    <td>{{ $task->content }}</td>
                    <td>{{ $task->created_at }}</td>
                    <td>
                        <button  class="btn btn-info edit" value="{{ $task->id }}">编辑</button>
                        <button class="btn btn-warning delete" value="{{ $task->id }}">删除</button>
                    </td>
                </tr>
            @endforeach
            </tbody>
        </table>
    </div>
    {{--Modal--}}
    <div class="modal fade" id="taskModal" tabindex="-1" role="dialog">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal"><span >&times;</span></button>
                    <h4 class="modal-title" id="task-title">编辑任务</h4>
                </div>
                <div class="modal-body">
                    <form id="task">
                        <div class="form-group">
                            <label for="tname" class="control-label">Name:</label>
                            <input id="tname" class="form-control" type="text">
                        </div>
                        <div class="form-group">
                            <label for="tcontent" class="control-label">Content:</label>
                            <textarea class="form-control" id="tcontent"></textarea>
                        </div>
                        {!! csrf_field() !!}
                    </form>
                </div>
                <div class="modal-footer">
                    <button type="button" id="tsave" class="btn btn-primary" value="update">提交</button>
                    <input type="hidden" id="tid" name="tid" value="-1">
                </div>
            </div>
        </div>
    </div>

</div>

<head></head>中插入:

<script src="{{ asset('js/app.js') }}"></script>

创建public/js/app.js

好了目前页面如图:
http://7xibui.com1.z0.glb.clouddn.com//2016/08/QQ%E6%88%AA%E5%9B%BE20160817151321.png

增删查改CRUD

下面我们来实现核心功能:增删查改。

在实现之前,我们记得添加CSRF_TOKEN,不然ajax的时候会出现500错误。

编辑resources/welcome.blade.php,在<form></form>中添加如下代码:

...
    </div>
    {!! csrf_field() !!}
</form>
...

增加

Task控制器中添加函数:

public function store(Request $request) {
    $this->validate($request, [
        'name' => 'required',
        'content' => 'required'
    ]);
    $task = new Task();
    $task->name = $request->get('name');
    $task->content = $request->get('content');
    $task->save();
    return response()->json($task);
}

删除

Task控制器中增加函数:

public function destroy($id) {
    $task = Task::find($id);
    $task->delete();
    return response()->json(['success']);
}

查、改

Task控制器中增加函数:

public function show($id) {
    $task = Task::find($id);
    return response()->json($task);
}
public function update(Request $request, $id) {
    $this->validate($request, [
        'name' => 'required',
        'content' => 'required'
    ]);
    $task = Task::find($id);
    $task->name = $request->get('name');
    $task->content = $request->get('content');
    $task->save();
    return response()->json($task);
}

app.js

最后的public/js/app.js如下:

$(document).ready(function () {
    url = '/task/';
    $.ajaxSetup({
        headers: {
            'X-CSRF-TOKEN': $('#task input[name="_token"]').val()
        }
    });

    $('#add').click(function () {
        $('#task-title').text('添加任务');
        $('#tsave').val('add');
        $('#taskModal').modal('show');
    });

    $('body').on('click', 'button.delete', function() {
        var tid = $(this).val();
        $.ajax({
            type: 'DELETE',
            url: url+tid,
            success: function (data) {
                console.log(data);
                $('#task'+tid).remove();
                toastr.success('删除成功!');
            },
            error: function (data, json, errorThrown) {
                console.log(data);
                var errors = data.responseJSON;
                var errorsHtml= '';
                $.each( errors, function( key, value ) {
                    errorsHtml += '<li>' + value[0] + '</li>';
                });
                toastr.error( errorsHtml , "Error " + data.status +': '+ errorThrown);
            }
        });
    });

    $('body').on('click', 'button.edit', function() {
        $('#task-title').text('编辑任务');
        $('#tsave').val('update');
        var tid = $(this).val();
        $('#tid').val(tid);
        $.get(url+tid, function (data) {
            console.log(url+tid);
            console.log(data);
            $('#tname').val(data.name);
            $('#tcontent').val(data.content);
        });

        $('#taskModal').modal('show');
    });

    $('#tsave').click(function () {
        if($('#tsave').val() == 'add') {
            turl = url;
            var type = "POST"; // add
        }
        else {
            turl = url + $('#tid').val();
            var type = "PUT"; // edit
        }
        var data = {
            name: $('#tname').val(),
            content: $('#tcontent').val()
        };

        $.ajax({
            type: type,
            url: turl,
            data: data,
            dataType: 'json',
            success: function (data) {
                console.log(data);
                $('#taskModal').modal('hide');
                $('#task').trigger('reset');
                var task = '<tr id="task' + data.id + '">' +
                    '<td>'+ data.id +'</td>' +
                    '<td>'+ data.name +'</td>' +
                    '<td>'+ data.content +'</td>' +
                    '<td>'+ data.created_at +'</td>' +
                    '<td><button class="btn btn-info edit" value="'+ data.id +'">编辑</button> <button class="btn btn-warning delete" value="'+ data.id +'">删除</button>'+ '</td>' +
                    '<tr>';
                console.log(task);
                if(type == 'POST') {
                    $('#task-list').append(task);
                    toastr.success('添加成功!');
                }
                else { // edit
                    $('#task'+data.id).replaceWith(task);
                    toastr.success('编辑成功!');
                }
            },
            error: function (data, json, errorThrown) {
                console.log(data);
                var errors = data.responseJSON;
                var errorsHtml= '';
                $.each( errors, function( key, value ) {
                    errorsHtml += '<li>' + value[0] + '</li>';
                });
                toastr.error( errorsHtml , "Error " + data.status +': '+ errorThrown);
            }
        });

    });

});

问题记录

写这个教程遇到的问题也蛮大的,记录下来。

路由问题

之前路由是这样的:

Route::resource('/', 'TaskController');

但是会出现404问题,于是只好重定向到/task中,问题解决。

Route::get('/', function () {
    return redirect('/task');
});

Route::resource('/task', 'TaskController');

新元素事件绑定问题

之前是这样绑定事件的:

$('.delete').click(function () {
    ...
}

但是会出现增加记录,无法点击触发事件的问题,将事件绑定到body解决:

$('body').on('click', 'button.delete', function() {
    ...
}

参考链接:Event binding on dynamically created elements?

响应Json

Laravel返回模型的时候默认以json方式返回,所以无需response()->json()