作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
如果你还不熟悉的话, GraphQL是一种用于与API交互的查询语言 提供了一些好处 与REST等可选架构相比. GraphQL在用作移动和单页面应用程序的端点时非常方便. GraphQL允许您相对轻松地查询请求中的嵌套和相关数据, 允许开发人员在与服务器的一次往返中获得所需的确切数据.
Laravel 是一个流行的,固执己见的PHP web框架. 它提供了许多内置工具来快速启动和运行应用程序, 但它也允许开发人员在需要时将自己的实现替换为Laravel的内置接口.
尽管GraphQL和Laravel的社区都在开源后迅速发展, 解释如何结合使用这两种技术的文档仍然很少.
因此,在本教程中,我将向您展示如何使用Laravel创建自己的GraphQL服务器.
在开始之前,我们需要熟悉我们正在尝试构建的项目. To do that, 我们将定义资源并创建GraphQL模式, 我们稍后将使用它来服务我们的API.
我们的应用程序将包含两个资源: Articles and Users. 这些资源将被定义为GraphQL模式中的对象类型:
type User {
id: ID!
name: String!
email: String!
文章(文章:!]!
}
type Article {
id: ID!
title: String!
content: String!
author: User!
}
查看模式,我们可以看到两个对象之间存在一对多关系. 用户可以写很多篇文章,每篇文章都有一个指定的作者(用户).
现在我们已经定义了对象类型, 我们需要一种方法来创建和查询我们的数据, 那么让我们定义查询和变异对象:
type Query {
user(id: ID!): User
users: [User!]!
article(id: ID!): Article
文章(文章:!]!
}
type Mutation {
createUser(名称:字符串!, email: String!,密码:字符串!): User
createArticle(标题:字符串!,内容:字符串!): Article
}
现在我们已经定义了GraphQL模式,让我们启动并运行Laravel项目. 让我们先通过Composer项目创建一个新的Laravel:
/ / / / / / / / / / / /
只是为了确保一切正常, 让我们启动服务器,确保我们看到Laravel的默认页面:
$ CD larvel -graphql
$ PHP工匠服务
Laravel development server started:
出于本文的目的,我们将使用SQLite. 因此,让我们对默认值进行以下更改 .env
file:
DB_CONNECTION = sqlite
# DB_HOST=
# DB_PORT=
# DB_DATABASE =数据库.sqlite
# DB_USERNAME=
# DB_PASSWORD=
接下来,让我们创建数据库文件:
$ touch ./数据库/数据库.sqlite
Laravel附带了一个用户模型和一些基本的迁移文件. 让我们快速加一个 api_token
列到我们的在我们的 CreateUsersTable
Laravel提供给我们的迁移文件:
/ /数据库迁移/ XXXX_XX_XX_000000_create_users_table.php
使用说明\迁移\ \数据库迁移;
数据库使用说明\ \模式\蓝图;
使用说明\ \外墙\模式的支持;
类CreateUsersTable扩展Migration
{
/**
*运行迁移.
*/
公共功能up()
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->string('api_token', 80)->unique()->nullable()->default(null);
$table->rememberToken();
$table->timestamps();
});
}
/**
*反向迁移.
*/
公共函数down()
{
模式:dropIfExists(“用户”);
}
}
在本文后面讨论授权时,我们将回到这个附加专栏. 现在让我们继续创建文章模型和一个迁移文件来创建相关的表:
$ php工匠制作:模型文章-m
Note: m选项为我们新创建的文章模型创建一个迁移文件.
让我们对生成的迁移文件做一些调整:
使用说明\ \外墙\模式的支持;
数据库使用说明\ \模式\蓝图;
使用说明\迁移\ \数据库迁移;
类CreateArticlesTable扩展迁移
{
/**
*运行迁移.
*
* @return void
*/
公共功能up()
{
Schema::create('articles', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->string('title');
$table->text('content');
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users');
});
}
/**
*反向迁移.
*
* @return void
*/
公共函数down()
{
模式:dropIfExists(“文章”);
}
}
我们添加了一个外键指向 id
on our users
表以及 title
and content
我们在GraphQL模式中定义的列.
现在我们已经定义了迁移文件,让我们继续在数据库中运行它们:
$ PHP工匠迁移
接下来,让我们通过定义必要的关系来更新模型:
app/User.php
namespace App;
使用说明\ \须申报的通知;
使用Illuminate\Foundation\Auth\User作为Authenticatable;
类User扩展Authenticatable
{
use Notifiable;
/**
*可大规模分配的属性.
*
* @var array
*/
Protected $fillable = [
'name', 'email', 'password',
];
// ...
/**
* @return \照亮\ \雄辩的\ \ HasMany关系数据库
*/
公共职能文章()
{
return $this->hasMany(Article::class);
}
}
app/Article.php
namespace App;
使用说明\雄辩的\ \数据库模型;
类Article扩展Model
{
/**
*可大规模分配的属性.
*
* @var array
*/
Protected $fillable = [
“标题”、“内容”,
];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
公共函数user()
{
return $this->belongsTo(User::class);
}
}
现在我们已经建立了模型和迁移,让我们建立数据库. 我们将从创建一些种子类开始 articles
and users
tables:
# # # # # # # # # # #
$ php工匠制作:种子ArticlesTableSeeder
接下来,让我们设置它们来插入一些虚拟数据到我们的SQLite数据库中:
数据库/ / UsersTableSeeder种子.php
use App\User;
数据库使用说明\ \播种机;
类UsersTableSeeder扩展了Seeder
{
/**
*运行数据库种子.
*/
公共函数run()
{
\ App \用户:截断();
$faker = \ faker \Factory::create();
$password = bcrypt('secret');
\ App \用户::创建([
'name' => $faker->name,
'email' => 'graphql@test.com',
'password' => $password,
]);
for ($i = 0; $i < 10; ++$i) {
\ App \用户::创建([
'name' => $faker->name,
'email' => $faker->email,
'password' => $password,
]);
}
}
}
数据库/ / ArticlesTableSeeder种子.php
use App\Article;
数据库使用说明\ \播种机;
类ArticlesTableSeeder扩展了Seeder
{
/**
*运行数据库种子.
*/
公共函数run()
{
\ App \文章:截断();
\ App \文章:使无防备();
$faker = \ faker \Factory::create();
\App\User::all()->each(function ($user) use ($faker) {
Foreach (range(1, 5) as $i) {
\ App \文章::创建([
'user_id' => $user->id,
'title' => $faker->sentence,
'content' => $faker->paragraphs(3, true),
]);
}
});
}
}
/数据库/ / DatabaseSeeder种子.php
数据库使用说明\ \播种机;
类databaseseder扩展了seder
{
/**
*播种应用程序的数据库.
*
* @return void
*/
公共函数run()
{
$this->call(UsersTableSeeder::class);
$this->call(ArticlesTableSeeder::class);
}
}
最后,让我们继续运行数据库种子器,将一些数据放入数据库:
$ PHP工匠db:种子
现在我们已经设置好了数据库和模型,是时候开始构建GraphQL服务器了. Currently, Laravel有几个可用的解决方案, 但是对于这篇文章, 我们要用 Lighthouse.
Lighthouse是我几年前创建的一个包,最近它得到了越来越多的社区的支持. 它允许开发人员使用Laravel快速设置GraphQL服务器,并且只需要很少的样板,同时也足够灵活,允许开发人员定制它以适应几乎任何项目的需求.
让我们从将包拉入我们的项目开始:
$ composer require nuwave/lighthouse:.1.*"
接下来,让我们发布Lighthouse的配置文件:
$ php artisan vendor:publish——provider="Nuwave\Lighthouse\LighthouseServiceProvider"——tag=config
Note: 您还可以选择发布Lighthouse的默认模式文件,只需删除 --tag=config
option. 但出于本文的目的,我们将从头创建模式文件.
如果我们看一下 配置/灯塔.php
你会注意到一个用于向Lighthouse注册模式文件的设置:
'schema' => [
'register' => base_path('graphql/schema.graphql'),
],
所以让我们继续创建schema文件,设置用户对象类型和查询:
$ mkdir graphql
$ touch ./graphql/schema.graphql
/graphql/schema.graphql
type User {
id: ID!
name: String!
email: String!
}
type Query {
user(id: ID! @eq):用户@find
users: [User!]! @all
}
您会注意到,我们的模式看起来与前面定义的模式类似,只是我们添加了一些标识符 模式指令.
让我们花点时间来分解我们定义的模式. 第一个定义是an object type called User
哪个和我们有关系 App\User
eloquent model. We defined the id
, name
and email
作为可以从我们的 User
models. 或者,这意味着 password
, created_at
and updated_at
列是无法从API查询的字段.
Next we have our Query
类型,它是进入API的入口点,可用于查询数据. 我们的第一个字段是 users
的数组 User
object types. The @all
指令告诉Lighthouse运行一个Eloquent查询,使用 User
建模并得到所有结果. 这将与运行以下命令相同:
$users = \App\User::all();
Note: 灯塔知道在 \App\User
命名空间,因为 namespaces
选项在其配置文件中定义.
查询类型上定义的第二个字段是call user
, which takes an id
作为参数,并返回一个 User
object type. 我们还添加了两个指令来帮助Lighthouse自动为我们构建查询并返回单个查询 User
model. The @eq
指令告诉Lighthouse在我们的 id
column, and the @find
指令指示Lighthouse返回单个结果. 要使用Laravel的查询生成器编写这个查询,它看起来像这样:
$user = \App\User::where('id', $args['id'])->first();
现在我们对Lighthouse如何使用我们的模式来创建查询有了一些了解, 让我们运行服务器并开始查询数据. 我们将从运行服务器开始:
$ PHP工匠服务
Laravel development server started:
查询GraphQL端点, 您可以在终端或标准客户端(如Postman)中运行cURL命令. However, 以获得GraphQL的全部好处(例如自动完成), 错误高亮显示, documentation, etc., we’ll use GraphQL操场 (版本下载 here).
启动Playground时,点击“URL Endpoint”选项卡,然后输入 http://localhost:8000/graphql 将GraphQL操场指向我们的服务器. 在编辑器的左边, 我们可以查询我们的数据, 那么让我们从查询我们在数据库中播种的所有用户开始:
{
users {
id
email
name
}
}
当您点击IDE中间的播放按钮(或单击 Ctrl+Enter),你会在右侧看到我们服务器的JSON输出,它看起来像这样:
{
"data": {
"users": [
{
"id": "1",
“电子邮件”:“graphql@test.com",
"姓名":"卡罗琳·鲍洛夫斯基"
},
{
"id": "2",
“电子邮件”:“kheaney@yahoo.com",
"姓名":"露易丝·雷诺"
},
{
"id": "3",
“电子邮件”:“奥兰多.sipes@yahoo.com",
"name": "Mrs. Dejah Wiza"
},
...
]
}
}
Note: 因为我们用Faker来建立数据库,数据库里的数据 email
and name
字段会有所不同.
现在让我们尝试查询单个用户:
{
user(id: 1) {
email
name
}
}
对于单个用户,我们将得到以下输出:
{
"data": {
"user": {
“电子邮件”:“graphql@test.com",
"姓名":"卡罗琳·鲍洛夫斯基"
}
}
}
像这样查询数据是很好的开始, 但是,在一个项目中,您不太可能想要查询 all 因此,让我们尝试添加一些分页. 当你穿过灯塔的广阔视野 内置的指令, we have a @paginate
指令对我们来说是可用的,所以让我们像这样更新schema的查询对象:
type Query {
user(id: ID! @eq):用户@find
users: [User!]! @paginate
}
如果我们重载GraphQL操场 (Ctrl/Cmd + R) and try our users
再次查询,您将注意到我们得到一条错误消息,说明 无法在UserPaginator类型上查询字段id
那么发生了什么呢?? 幕后故事, Lighthouse操作我们的模式,让我们获得一组经过分页的结果,并通过更改类的返回类型来实现 users
field.
让我们通过在GraphQL操场的“文档”选项卡中检查我们的模式来仔细看看. 如果你看一下 users
字段,它返回a UserPaginator
它返回一个用户数组和一个定义的Lighthouse PaginatorInfo
type:
类型UserPaginator {
paginatorInfo: paginatorInfo!
data: [User!]!
}
type PaginatorInfo {
count: Int!
currentPage: Int!
firstItem: Int
hasMorePages:布尔!
lastItem: Int
lastPage: Int!
perPage: Int!
total: Int!
}
如果您熟悉Laravel的内置分页,那么 PaginatorInfo
这种类型对你来说可能很熟悉. So, 查询两个用户, 获取系统中的用户总数, 检查一下我们有更多的页面可以循环浏览, 我们将发送以下查询:
{
users(count:2) {
paginatorInfo {
total
hasMorePages
}
data {
id
name
email
}
}
}
这将为我们提供以下回复:
{
"data": {
"users": {
" paginatorInfo ": {
"total": 11,
“hasMorePages”:真的
},
"data": [
{
"id": "1",
"姓名":"卡罗琳·鲍洛夫斯基";
“电子邮件”:“graphql@test.com"
},
{
"id": "2",
"姓名":"露易丝·雷诺";
“电子邮件”:“kheaney@yahoo.com"
},
]
}
}
}
通常,在开发应用程序时,很多数据都是相关的. In our case, a User
can write many Articles
,让我们将那个关系添加到User类型并定义 Article
type:
type User {
id: ID!
name: String!
email: String!
文章(文章:!]! @hasMany
}
type Article {
id: ID!
title: String!
content: String!
}
这里,我们使用了另一个Lighthouse提供的模式指令 @hasMany
,它告诉灯塔我们 User
model has a \照亮\ \雄辩的\ \ HasMany关系数据库
与政府的关系 Article
model.
现在让我们来查询新定义的关系:
{
user(id:1) {
articles {
id
title
}
}
}
这将为我们提供以下响应:
{
"data": {
"user": {
"articles": [
{
"id": "1",
"title": "Aut velit et temporibus但et tempora sint "."
},
{
"id": "2",
“title”:“Voluptatem是劳动,是voluptas。."
},
{
"id": "3",
"title": "Beatae sit et maximum consequence et natus toam "."
},
{
"id": "4",
"title": "Corrupti beatae cumque控告"."
},
{
"id": "5",
"title": "Aperiam quidem sit esse rem sed cupiditate "."
}
]
}
}
}
最后,让我们把关系反过来,加上 author
与我们的关系 Article
对象类型使用Lighthouse的 @belongsTo
Schema指令以及更新我们的 Query
:
type Article {
id: ID!
title: String!
content: String!
author: User! @belongsTo(关系:“用户”)
}
type Query {
user(id: ID! @eq):用户@find
users: [User!]! @paginate
article(id: ID! @eq):文章@find
文章(文章:!]! @paginate
}
您将看到我们添加了一个可选的 relation
argument to the @belongsTo
directive. 这告诉灯塔使用 Articles
model’s user
关系,并将其分配给 author
field.
现在让我们查询文章列表并获取其关联作者:
{
文章(数量:2){
paginatorInfo {
total
hasMorePages
}
data {
id
title
author {
name
email
}
}
}
}
我们应该从服务器得到以下内容:
{
"data": {
"articles": {
" paginatorInfo ": {
"total": 55,
“hasMorePages”:真的
},
"data": [
{
"id": "1",
"title": "Aut velit et temporibus但et tempora sint ".",
"author": {
"姓名":"卡罗琳·鲍洛夫斯基";
“电子邮件”:“graphql@test.com"
}
},
{
"id": "2",
“title”:“Voluptatem是劳动,是voluptas。.",
"author": {
"姓名":"卡罗琳·鲍洛夫斯基";
“电子邮件”:“graphql@test.com"
}
}
]
}
}
}
现在我们可以查询数据了,让我们创建一些突变来创建一些新的用户和文章. 我们将从我们的用户模型开始:
type Mutation {
createUser(
name: String!
email: String! @rules(apply: ["email", "unique:users"])
密码:字符串! @bcrypt @rules(apply: ["min:6"])
): User @create
}
现在让我们分解这个模式定义. 我们创造了一个变种,叫做 createUser
它有三个参数(name
, email
, and password
). 我们应用了 @rules
给我们双方的指示 email
and password
arguments. 这可能看起来有点熟悉,因为它类似于 validation logic Laravel提供了它的控制器.
接下来,我们把 @bcrypt
directive to our password
field. 这将在密码传递给新创建的模型之前对其进行加密.
最后,为了帮助我们创建新的模型,Lighthouse提供了一个 @create
Schema指令,它将接受我们定义的参数并创建一个新模型. 在控制器中执行相同的逻辑如下所示:
名称空间的应用程序\ Http \控制器;
使用说明\ Http \请求;
类UserController扩展控制器
{
/**
*创建一个新用户.
*
* @param \Illuminate\Http\Request $ Request
* @return \Illuminate\Http\Response
*/
存储(请求)
{
$data = $this->validate($request, [
'email' => ['email', 'unique:users'],
'password' => ['min:6']
]);
$user = \App\ user::create($data);
return response()->json(['user' => $user]);
}
}
现在我们已经设置了createUser突变字段, 让我们继续在GraphQL操场中运行它:
mutation {
createUser(
name:"John Doe"
email:"john.doe@example.com"
密码:“秘密”
) {
id
name
email
}
}
我们应该得到以下输出:
{
"data": {
"createUser": {
"id": "12",
"name": "John Doe",
"email": "john.doe@example.com"
}
}
}
因为我们需要加上a user_id
to our Article
现在是学习GraphQL/Lighthouse中的身份验证和授权的好时机.
要对用户进行身份验证,我们需要向他们提供一个 api_token
,所以让我们创建一个突变来处理它,我们将添加 @field
指令,将Lighthouse指向一个将处理逻辑的自定义解析器. 我们将解析器设置为与 定义控制器 在Laravel中使用 resolver
argument.
With the @field
指令定义时,我们会告诉灯塔 login
变异是运行时,使用的 createToken
method on our App \ GraphQL \ \ AuthMutator突变
class:
type Mutation {
# ...
login(
email: String!
密码:字符串!
):字符串@field(解析器:"AuthMutator@resolve")
}
Note: 您不需要在这里包含整个名称空间. In the lighthouse.php
在配置文件中,您将看到我们为我们的突变设置了命名空间 应用\ \ GraphQL \ \突变
但是,如果愿意,您可以使用完整的名称空间.
让我们使用Lighthouse的生成器来创建新的mutator类:
$ php工匠灯塔:突变AuthMutator
接下来,让我们像这样更新解析器函数:
名称空间的应用程序\ GraphQL \突变;
使用说明\ \ Arr的支持;
使用说明\ \ Str的支持;
使用说明\ \外墙\身份验证的支持;
使用GraphQL \ \ \ ResolveInfo定义类型;
使用Nuwave \灯塔\ \ \ GraphQLContext合同的支持;
类AuthMutator
{
/**
*返回字段的值.
*
* @param null $rootValue通常包含从父字段返回的结果. 在这种情况下,它总是' null '.
* @param mixed[] $args传入字段的参数.
* @param \Nuwave\Lighthouse\Support\Contracts\GraphQLContext $context在单个查询的所有字段之间共享的任意数据.
* @param \GraphQL\Type\Definition\ResolveInfo $ ResolveInfo查询本身的信息, 例如执行状态, the field name, 从根目录到字段的路径, and more.
* @return mixed
*/
公共函数解析($rootValue, array $args, GraphQLContext $context, ResolveInfo $ ResolveInfo)
{
$credentials = Arr::only($args, ['email', 'password']);
if (Auth::once($credentials)) {
$token = Str::random(60);
$user = auth()->user();
$user->api_token = $token;
$user->save();
return $token;
}
return null;
}
}
现在我们已经设置好了解析器, 让我们测试一下,并尝试在GraphQL操场中使用以下突变来获得一个API令牌:
mutation {
登录(电子邮件:“graphql@test.com”,密码:“秘密”)
}
我们应该得到这样一个令牌:
{
"data": {
“登录”:“VJCz1DCpmdvB9WatqvWbXBP2RN8geZQlrQatUnWIBJCdbAyTl3UsdOuio3VE”
}
}
Note: 一定要复制从登录突变返回的令牌,以便稍后使用它.
接下来,让我们添加一个查询字段,它将返回经过身份验证的用户,以确保我们的逻辑正常工作. 我们将添加一个名为 me
使用灯塔的 @auth
指令返回当前已验证的用户. 我们还会设置 guard
参数等于 api
因为这就是我们验证用户的方式.
type Query {
# ...
我:用户@auth(守卫:“api”)
}
现在让我们运行查询. 在GraphQL操场中, 你可以通过双击底部的“Http headers”选项卡来设置你的请求头. 我们使用JSON对象添加标题, 因此,为每个请求添加一个承载令牌, 你可以补充以下内容:
{
"授权":"Bearer VJCz1DCpmdvB9WatqvWbXBP2RN8geZQlrQatUnWIBJCdbAyTl3UsdOuio3VE"
}
Note: 将承载令牌替换为运行时收到的令牌 login query.
现在让我们运行 me
query:
{
me {
email
articles {
id
title
}
}
}
我们应该得到这样的输出:
{
"data": {
"me": {
“电子邮件”:“graphql@test.com",
"articles": [
{
"id": "1",
“title”:“Rerum pecpecatis et quos ococatationem”."
},
{
"id": "2",
"title": " place quia cumque laudantim option voluptatem sed qui "."
},
{
"id": "3",
“title”:“option voluptatem et itaque sit animi”."
},
{
"id": "4",
"title": " except in and qui dolor and perspicitis adipisci "."
},
{
"id": "5",
"title": "Qui nemo blditiis sed fugit consequatur "."
}
]
}
}
}
现在我们知道我们的身份验证工作正常, 让我们创建最后一个突变,使用当前经过身份验证的用户创建一篇文章. We’ll use the @field
指令将Lighthouse指向我们的解析器,我们还将包含一个 @middleware
指令,以确保用户已登录.
type Mutation {
# ...
createArticle(标题:字符串!,内容:字符串!): Article
@field(解析器:“ArticleMutator@create”)
@middleware(检查:["身份验证:api "])
}
首先,让我们生成一个突变类:
$ php工匠灯塔:突变ArticleMutator
接下来,让我们用以下逻辑更新这个mutator:
名称空间的应用程序\ GraphQL \突变;
使用Nuwave \灯塔\ \ \ GraphQLContext合同的支持;
类ArticleMutator
{
/**
*返回字段的值.
*
* @param null $rootValue
* @param mixed[] $args
* @param \Nuwave\Lighthouse\Support\Contracts\GraphQLContext $context
* @return mixed
*/
public function create($rootValue, array $args, GraphQLContext $context)
{
$article = new \App\ article ($args);
$context->user()->articles()->save($article);
return $article;
}
}
Note: 我们重命名了默认值 resolve
function to create
. 您不需要为每个解析器创建一个新类. 相反,如果更有意义的话,您可以将逻辑分组在一起.
最后,让我们运行新的突变并检查输出. 一定要保存好 Authorization
头从我们之前的查询“HTTP头”选项卡:
mutation {
createArticle(
题目:“用Laravel构建GraphQL服务器”
内容:“以防你目前不熟悉它, GraphQL是一种用于与API交互的查询语言..."
) {
id
author {
id
email
}
}
}
我们应该得到以下输出:
{
"data": {
" createArticle ": {
"id": "56",
"author": {
"id": "1",
“电子邮件”:“graphql@test.com"
}
}
}
}
回顾一下,我们已经利用Lighthouse为我们的Laravel项目创建了一个GraphQL服务器. 我们使用了一些内置的模式指令, 已创建的查询和变更, 负责授权和认证工作.
Lighthouse允许您做更多的事情(例如允许您创建自己的 自定义模式指令),但出于本文的目的,我们坚持基础知识,并且我们能够使用相当少的样板文件建立并运行GraphQL服务器.
下次您需要为移动或单页面应用程序设置API时,请务必考虑 GraphQL 作为查询数据的一种方式!
实现GraphQL规范并公开API端点供客户端查询的服务器.
GraphQL是强类型的, self-documenting, 并支持声明式数据获取, 它允许客户查询他们需要的确切数据,并使构建强大而深入的开发人员工具成为可能.
GraphQL通常用于需要查询相关数据的场景,或者在多次往返服务器可能对客户体验产生负面影响的环境中使用.
Laravel是一个用PHP语言构建的web框架. 它的需求量很大,因为它能够通过为开发人员提供一个详尽的工具包来简化开发体验,以便快速使用和迭代.
Laravel提供了许多开箱即用的必要工具来构建web应用程序. 它也是开源的, 这使得社区中的人能够为现代web开发中的许多常见边缘情况做出贡献.
Laravel通常被认为是一个后端web服务器. However, 它包括一个名为“Blade”的富有表现力的模板引擎,,这就简化了前端的开发.
Currently, Laravel是所有语言中最流行的web框架之一,它的社区正在继续快速增长.
克里斯托弗•摩尔是一个拥有超过7年经验的全栈PHP开发人员. 他专门从事Laravel和Ember的开发.
14
世界级的文章,每周发一次.
世界级的文章,每周发一次.