Eloquent ORM Laravel 5.3 创建 配置与使用

1、简介

Laravel 自带的 Eloquent ORM 提供了一个美观、简单的与数据库打交道的 ActiveRecord 实现,每张数据表都对应一个与该表进行交互的“模型”,模型允许你在表中进行数据查询,以及插入、更新、删除等操作。

在开始之前,确保在config/database.php文件中配置好了数据库连接。更多关于数据库配置的信息,请查看文档。

2、定义模型

作为开始,让我们创建一个 Eloquent 模型,模型通常位于app目录下,你也可以将其放在其他可以被composer.json文件自动加载的地方。所有Eloquent模型都继承自 Illuminate\Database\Eloquent\Model类。

创建模型实例最简单的办法就是使用 Artisan 命令make:model

php artisan make:model User

如果你想要在生成模型时生成数据库迁移,可以使用--migration-m选项:

php artisan make:model User --migration
php artisan make:model User -m

Eloquent 模型约定

现在,让我们来看一个 Flight 模型类例子,我们将用该类获取和存取数据表flights中的信息:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model{
    //
}

表名

注意我们并没有告诉 Eloquent 我们的Flight模型使用哪张表。默认规则是模型类名的复数作为与其对应的表名,除非在模型类中明确指定了其它名称。所以,在本例中,Eloquent 认为Flight模型存储记录在flights表中。你也可以在模型中定义table属性来指定自定义的表名:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model{
    /**
     * 关联到模型的数据表
     *
     * @var string
     */
    protected $table = 'my_flights';
}

主键

Eloquent 默认每张表的主键名为id,你可以在模型类中定义一个$primaryKey属性来覆盖该约定。

此外,Eloquent默认主键字段是自增的整型数据,这意味着主键将会被自动转化为int类型,如果你想要使用非自增或非数字类型主键,必须在对应模型中设置$incrementing属性为false

时间戳

默认情况下,Eloquent 期望created_atupdated_at已经存在于数据表中,如果你不想要这些 Laravel 自动管理的列,在模型类中设置$timestamps属性为false

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model{
    /**
     * 表明模型是否应该被打上时间戳
     *
     * @var bool
     */
    public $timestamps = false;
}

如果你需要自定义时间戳格式,设置模型中的$dateFormat属性。该属性决定日期被如何存储到数据库中,以及模型被序列化为数组或 JSON 时日期的格式:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model{
    /**
     * 模型日期列的存储格式
     *
     * @var string
     */
    protected $dateFormat = 'U';
}

数据库连接
 
默认情况下,所有的 Eloquent 模型使用应用配置中的默认数据库连接,如果你想要为模型指定不同的连接,可以通过$connection 属性来设置:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model{
    /**
     * The connection name for the model.
     *
     * @var string
     */
    protected $connection = 'connection-name';
}

3、获取模型

创建完模型及其关联的数据表后,就要准备从数据库中获取数据。将Eloquent模型看作功能强大的查询构建器,你可以使用它来流畅的查询与其关联的数据表。例如:

<?php

use App\Flight;

$flights = App\Flight::all();

foreach ($flights as $flight) {
    echo $flight->name;
}

添加额外约束

Eloquent 的all方法返回模型表的所有结果,由于每一个Eloquent模型都是一个查询构建器,你还可以添加约束条件到查询,然后使用get方法获取对应结果:

$flights = App\Flight::where('active', 1)
               ->orderBy('name', 'desc')
               ->take(10)
               ->get();

注意:由于 Eloquent 模型本质上就是查询构建器,你可以在Eloquent查询中使用查询构建器的所有方法。

集合

对 Eloquent 中获取多个结果的方法(比如allget)而言,其返回值是Illuminate\Database\Eloquent\Collection的一个实例,Collection类提供了多个有用的函数来处理Eloquent结果集:

$flights = $flights->reject(function ($flight) {
    return $flight->cancelled;
});

当然,你也可以像数组一样循环遍历该集合:

foreach ($flights as $flight) {
    echo $flight->name;
}

组块结果集

如果你需要处理成千上万个 Eloquent 结果,可以使用chunk命令。chunk方法会获取一个“组块”的 Eloquent 模型,并将其填充到给定闭包进行处理。使用chunk方法能够在处理大量数据集合时有效减少内存消耗:

Flight::chunk(200, function ($flights) {
    foreach ($flights as $flight) {
        //
    }
});

传递给该方法的第一个参数是你想要获取的“组块”数目,闭包作为第二个参数被调用用于处理每个从数据库获取的区块数据。

使用游标

cursor
方法允许你使用游标迭代处理数据库记录,一次只执行单个查询,在处理大批量数据时,cursor方法可大幅减少内存消耗:

foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
    //
}

4、获取单个模型/聚合

当然,除了从给定表中获取所有记录之外,还可以使用findfirst获取单个记录。这些方法返回单个模型实例而不是返回模型集合:

// 通过主键获取模型...
$flight = App\Flight::find(1);
// 获取匹配查询条件的第一个模型...
$flight = App\Flight::where('active', 1)->first();

还可以通过传递主键数组来调用find方法,这将会返回匹配记录集合:

$flights = App\Flight::find([1, 2, 3]);

Not Found 异常

有时候你可能想要在模型找不到的时候抛出异常,这在路由或控制器中非常有用,findOrFailfirstOrFail方法会获取查询到的第一个结果。然而,如果没有任何查询结果,Illuminate\Database\Eloquent\ModelNotFoundException异常将会被抛出:

$model = App\Flight::findOrFail(1);
$model = App\Flight::where('legs', '>', 100)->firstOrFail();

如果异常没有被捕获,那么HTTP 404 响应将会被发送给用户,所以在使用这些方法的时候没有必要对返回404响应编写明确的检查:

Route::get('/api/flights/{id}', function ($id) {
    return App\Flight::findOrFail($id);
});

获取聚合

当然,你还可以使用查询构建器聚合方法,例如countsummax,以及其它查询构建器提供的聚合方法。这些方法返回计算后的结果而不是整个模型实例:

$count = App\Flight::where('active', 1)->count();
$max = App\Flight::where('active', 1)->max('price');

5、插入/更新模型

插入

想要在数据库中插入新的记录,只需创建一个新的模型实例,设置模型的属性,然后调用save方法:

<?php

namespace App\Http\Controllers;

use App\Flight;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class FlightController extends Controller{
    /**
     * 创建一个新的航班实例
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // Validate the request...

        $flight = new Flight;

        $flight->name = $request->name;

        $flight->save();
    }
}

在这个例子中,我们只是简单分配HTTP请求中的name参数值给App\Flight模型实例的那么属性,当我们调用save方法时,一条记录将会被插入数据库。created_atupdated_at时间戳在save方法被调用时会自动被设置,所以没必要手动设置它们。

更新

save方法还可以用于更新数据库中已存在的模型。要更新一个模型,应该先获取它,设置你想要更新的属性,然后调用save方法。同样,updated_at时间戳会被自动更新,所以没必要手动设置其值:

$flight = App\Flight::find(1);
$flight->name = 'New Flight Name';
$flight->save();

更新操作还可以同时修改给定查询提供的多个模型实例,在本例中,所有有效且destination=San Diego的航班都被标记为延迟:

App\Flight::where('active', 1)
          ->where('destination', 'San Diego')
          ->update(['delayed' => 1]);

update方法要求以数组形式传递键值对参数,代表着数据表中应该被更新的列。

注:通过Eloquent进行批量更新时,savedupdated模型事件将不会在更新模型时触发。这是因为在进行批量更新时并没有从数据库获取模型。

批量赋值

还可以使用create方法保存一个新的模型。该方法返回被插入的模型实例。但是,在此之前,你需要指定模型的fillableguarded属性,因为所有Eloquent模型都通过批量赋值(Mass Assignment)进行保护。

当用户通过 HTTP 请求传递一个不被期望的参数值时就会出现安全隐患,然后该参数以不被期望的方式修改数据库中的列值。例如,恶意用户通过 HTTP 请求发送一个is_admin参数,然后该参数映射到模型的create方法,从而允许用户将自己变成管理员。

所以,你应该在模型中定义哪些属性是可以进行赋值的,使用模型上的$fillable属性即可实现。例如,我们设置Flight模型上的name属性可以被赋值:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model{
    /**
     * 可以被批量赋值的属性.
     *
     * @var array
     */
    protected $fillable = ['name'];
}

设置完可以被赋值的属性之后,我们就可以使用create方法在数据库中插入一条新的记录。create方法返回保存后的模型实例:

$flight = App\Flight::create(['name' => 'Flight 10']);

黑名单属性

$fillable就像是可以被赋值属性的“白名单”,还可以选择使用$guarded$guarded属性包含你不想被赋值的属性数组。所以不被包含在其中的属性都是可以被赋值的,因此,$guarded功能就像“黑名单”。当然,这两个属性你只能同时使用其中一个——而不能一起使用,因为它们是互斥的:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model{
    /**
     * 不能被批量赋值的属性
     *
     * @var array
     */
    protected $guarded = ['price'];
}

如果你想要让所有属性都是可批量赋值的,可以将$guarded属性设置为空数组:

/**
 * The attributes that aren't mass assignable.
 *
 * @var array
 */
protected $guarded = [];

其它创建方法

还有其它两种可以用来创建模型的方法:firstOrCreatefirstOrNewfirstOrCreate方法先尝试通过给定列/值对在数据库中查找记录,如果没有找到的话则通过给定属性创建一个新的记录。

firstOrNew方法和firstOrCreate方法一样先尝试在数据库中查找匹配的记录,如果没有找到,则返回一个的模型实例。注意通过firstOrNew方法返回的模型实例并没有持久化到数据库中,你还需要调用save方法手动持久化:

// 通过属性获取航班, 如果不存在则创建...
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);
// 通过属性获取航班, 如果不存在初始化一个新的实例...
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);

6、删除模型

要删除一个模型,调用模型实例上的delete方法:

$flight = App\Flight::find(1);
$flight->delete();

通过主键删除模型

在上面的例子中,我们在调用delete方法之前从数据库中获取该模型,然而,如果你知道模型的主键的话,可以调用destroy方法直接删除而不需要获取它:

App\Flight::destroy(1);
App\Flight::destroy([1, 2, 3]);
App\Flight::destroy(1, 2, 3);

通过查询删除模型

当然,你还可以通过查询删除多个模型,在本例中,我们删除所有被标记为无效的航班:

$deletedRows = App\Flight::where('active', 0)->delete();

注:通过Eloquent进行批量删除时,deletingdeleted模型事件在删除模型时不会被触发,这是因为在进行模型删除时不会获取模型。

软删除

除了从数据库删除记录外,Eloquent还可以对模型进行“软删除”。当模型被软删除后,它们并没有真的从数据库删除,而是在模型上设置一个deleted_at属性并插入数据库,如果模型有一个非空deleted_at值,那么该模型已经被软删除了。要启用模型的软删除功能,可以使用模型上的Illuminate\Database\Eloquent\SoftDeletestrait并添加deleted_at列到$dates属性:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Flight extends Model{
    use SoftDeletes;

    /**
     * 应该被调整为日期的属性
     *
     * @var array
     */
    protected $dates = ['deleted_at'];
}

当然,应该添加deleted_at列到数据表。Laravel Schema构建器包含一个帮助函数来创建该列:

Schema::table('flights', function ($table) {
    $table->softDeletes();
});

现在,当调用模型的delete方法时,deleted_at列将被设置为当前日期和时间,并且,当查询一个使用软删除的模型时,被软删除的模型将会自动从查询结果中排除。

判断给定模型实例是否被软删除,可以使用trashed方法:

if ($flight->trashed()) {
    //
}

查询被软删除的模型

包含软删除模型

正如上面提到的,软删除模型将会自动从查询结果中排除,但是,如果你想要软删除模型出现在查询结果中,可以使用withTrashed方法:

$flights = App\Flight::withTrashed()
                ->where('account_id', 1)
                ->get();

withTrashed方法也可以用于关联查询中:

$flight->history()->withTrashed()->get();

只获取软删除模型

onlyTrashed方法之获取软删除模型:

$flights = App\Flight::onlyTrashed()
                ->where('airline_id', 1)
                ->get();

恢复软删除模型

有时候你希望恢复一个被软删除的模型,可以使用restore方法:

$flight->restore();

你还可以在查询中使用restore方法来快速恢复多个模型:

App\Flight::withTrashed()
        ->where('airline_id', 1)
        ->restore();

withTrashed方法一样,restore方法也可以用于关联查询:

$flight->history()->restore();

永久删除模型

有时候你真的需要从数据库中删除一个模型,可以使用forceDelete方法:

// 强制删除单个模型实例...
$flight->forceDelete();
// 强制删除所有关联模型...
$flight->history()->forceDelete();

7、查询作用域

全局作用域

全局作用域允许我们为给定模型的所有查询添加条件约束。Laravel 自带的软删除功能就使用了全局作用域来从数据库中拉出所有没有被删除的模型。编写自定义的全局作用域可以提供一种方便的、简单的方式来确保给定模型的每个查询都有特定的条件约束。

编写全局作用域

自定义全局作用域很简单,首先定义一个实现 Illuminate\Database\Eloquent\Scope 接口的类,该接口要求你实现一个方法:apply。需要的话可以在 apply 方法中添加 where 条件到查询:

<?php

namespace App\Scopes;

use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class AgeScope implements Scope{
    /**
     * Apply the scope to a given Eloquent query builder.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        return $builder->where('age', '>', 200);
    }
}

注:Laravel应用默认并没有为作用域预定义文件夹,所以你可以按照自己的喜好在app目录下创建Scopes目录。

应用全局作用域

要将全局作用域分配给模型,需要重写给定模型的 boot 方法并使用 addGlobalScope 方法:

<?php

namespace App;

use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;

class User extends Model{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope(new AgeScope);
    }
}

添加作用域后,如果使用 User::all() 查询则会生成如下SQL语句:

select * from `users` where `age` > 200

匿名的全局作用域

Eloquent还允许我们使用闭包定义全局作用域,这在实现简单作用域的时候特别有用,这样的话,我们就没必要定义一个单独的类了:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class User extends Model{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('age', function(Builder $builder) {
            $builder->where('age', '>', 200);
        });
    }
}

我们还可以通过以下方式移除全局作用:

User::withoutGlobalScope('age')->get();

移除全局作用域

如果想要在给定查询中移除指定全局作用域,可以使用 withoutGlobalScope

User::withoutGlobalScope(AgeScope::class)->get();

如果你想要移除某几个或全部全局作用域,可以使用 withoutGlobalScopes 方法:

User::withoutGlobalScopes()->get();
User::withoutGlobalScopes([FirstScope::class, SecondScope::class])->get();

本地作用域

本地作用域允许我们定义通用的约束集合以便在应用中复用。例如,你可能经常需要获取最受欢迎的用户,要定义这样的一个作用域,只需简单在对应Eloquent模型方法前加上一个scope前缀。

作用域总是返回查询构建器实例:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model{
    /**
     * 只包含活跃用户的查询作用域
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }

    /**
     * 只包含激活用户的查询作用域
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeActive($query)
    {
        return $query->where('active', 1);
    }
}

使用本地作用域

作用域被定义好了之后,就可以在查询模型的时候调用作用域方法,但调用时不需要加上scope前缀,你甚至可以在同时调用多个作用域,例如:

$users = App\User::popular()->active()->orderBy('created_at')->get();

动态作用域

有时候你可能想要定义一个可以接收参数的作用域,你只需要将额外的参数添加到你的作用域即可。作用域参数应该被定义在$query参数之后:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model{
    /**
     * 只包含给用类型用户的查询作用域
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeOfType($query, $type)
    {
        return $query->where('type', $type);
    }
}

现在,你可以在调用作用域时传递参数了:

$users = App\User::ofType('admin')->get();

8、事件

Eloquent模型可以触发事件,允许你在模型生命周期中的多个时间点调用如下这些方法:creatingcreatedupdatingupdatedsavingsaved,deletingdeletedrestoringrestored。事件允许你在一个指定模型类每次保存或更新的时候执行代码。

基本使用

一个新模型被首次保存的时候,creatingcreated事件会被触发。如果一个模型已经在数据库中存在并调用save方法,updating/updated事件会被触发,无论是创建还是更新,saving/saved事件都会被调用。

举个例子,我们在服务提供者中定义一个Eloquent事件监听器,在事件监听器中,我们会调用给定模型的isValid方法,如果模型无效会返回false。如果从Eloquent事件监听器中返回false则取消save/update操作:

<?php

namespace App\Providers;

use App\User;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider{
    /**
     * 启动所有应用服务
     *
     * @return void
     */
    public function boot()
    {
        User::creating(function ($user) {
            return $user->isValid();
        });
    }

    /**
     * 注册服务提供者.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

观察者

如果你在给定模型中监听多个事件,可以使用观察者来对所有监听器进行分组,观察者类拥有反射你想要监听的Eloquent事件对应的方法名,每个方法接收模型作为唯一参数。Laravel并没有为监听器提供默认目录,所以你可以创建任意目录来存放观察者类:

<?php

namespace App\Observers;

use App\User;

class UserObserver
{
    /**
     * Listen to the User created event.
     *
     * @param  User  $user
     * @return void
     */
    public function created(User $user)
    {
        //
    }

    /**
     * Listen to the User deleting event.
     *
     * @param  User  $user
     * @return void
     */
    public function deleting(User $user)
    {
        //
    }
}

要监听观察者,使用你想要观察模型的observe方法,你可以在某个服务提供者的boot方法中注册观察者,在本例中,我们在AppServiceProvider中注册观察者:

<?php

namespace App\Providers;

use App\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        User::observe(UserObserver::class);
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

有一种爱,是两棵树的守望

每一天都有新鲜的感悟,每一刻都拥有别样的心情.
爱情文章,与你一起分享感人的故事和爱情的箴言.
写下你的故事,一起分享沉淀于记忆深处的一种感动

那年,男孩和女孩在北方一所重点大学里读书,他们是一对让人羡慕的情侣,他写一首好诗,她画一手好画,人们都说他们是“金童玉女。”
男孩来自江南小镇,女孩是地道的北京女孩,他们初见,就如宝玉初见黛玉:“这个妹妹,我是见过的。”
相恋四年,毕业的时候,女孩把男孩带回家。母亲问他的家世,男孩一五一十说了。女孩惊觉自己的母亲变了脸色,然后拂袖而去,下了逐客令。
“怎么了?”女孩心里忐忑地问母亲。
母亲说,“文化大革命”的时候搞武斗,是男孩的父亲把她父亲搞死的,那时,女孩还小。母亲说:“你能嫁给他吗?你嫁给他,我宁可撞死。”
男孩不相信,回到南方小城,疯了似的去问父亲。父亲沉默很久才说:“‘文化大革命’那阵太乱了,有些事,说不清……”之后是长久的沉默。
刹间江河逆转,一对相恋的人,因为上一辈人的恩怨就要画上句号。
怎能肯心甘?女孩跪在母亲面前,求母亲放爱一条生路。母亲说:“除非我死,否则永远不可能。”母亲为她守了20多年寡,她如何舍得这如血亲情?
女孩绝望了,哭着对男孩说分手:“除了你,我一辈子不嫁。我等你,哪怕,从青丝,到白头。”男孩泪流满面地抱着她:“除了你,我谁也不娶,哪怕等到来世。”那是在上世纪80年代,那是爱情誓言。他们相约,一辈子不分开,永远为对方坚守爱情。
毕业五年后,他们依然我行我素,根本不理父母相逼:有人提亲,他们都一一拒绝,他们心中的恋人只是对方。后来,他们偷偷约会,背着双方父母,因为,空间怎么会隔断彼此间的爱情啊!
这五年,女孩在北方,男孩在南方。每隔两个月,她就会坐火车去找他,从北京坐到那个小城,有时只买一张硬座,只为省下点钱为他买些补品。他太瘦了,她看着心疼。
这一奔波,就是五年。
五年,从北京到小城,有着女孩一路的爱和欢喜,好背着母亲做这一切,只说是出差,其实,不过是看一眼远在南方的恋人。
28岁那年,男孩来找她了:“ 我们私奔,或者,一起殉情吧!”原来,他家里出了事,母亲去世了,他是独子,父亲给他跪下说:“儿子,你结婚吧,我求求你,咱家的香火不能断了呀!”为了让他结婚,父亲长跪不起!男孩坐了十几小时的火车来找她,想和她一起私奔。
女孩沉默了。这份爱情,代价太大了,她不能因为自己的爱情伤了他父亲的心,这样的固执虽然忠贞,但多么自私呀!
“不!”女孩说,“我不和你私奔,你没那个自由!我也不和你殉情,你必须照顾风烛残年的老父亲。去吧,找个好姑娘结婚吧,我不怪你。因为,你的幸福,就是我的幸福。”
男孩抱住她,放声痛哭,似杜鹃的啼血呜咽。他没想到,自己心爱的姑娘是这样的大度,为了他一家人的幸福,居然对爱放了手。他劝她:“你也结婚吧,别等我了,来生吧,来生,我一定娶你。”
女孩摇摇头:“此一生,再难与人他人相逢相知。我就当棵守望的木棉,站在风中,等你!”
最后一面,男孩送给女孩一枚双玉蝉,珍贵的祖母绿,是他家的传世珍宝。两只蝉,并肩而立,那样痴情地看着对方。男孩说:“虽然不是价值连城,等你老了,不能动了,就把它卖掉,它,可以养着你!看到它,就是看到我了。”
女孩扑入他的怀中恸哭,这个男人,连她的老年都想到了,怕她一个过不下去,把传世珍宝给了她。这一生,爱一场,值了!
女孩送给男孩的礼物是一幅画,那是她画得最好的一幅画——两棵木棉树,开满了花萼,一朵又一朵。她深情地说:“那是我的盼望,盼望来生,我是其中一朵,而你把我摘下。”
结婚那天,男孩把画挂在新房里,泪流满面。那两棵木棉树,一棵是他,一棵是她呀。她没有离开,在他的心里,在他的灵魂里。
两个相爱的人相约永不再见,永不再联系。是因为,善良的女孩想让他把一颗心扑到家里。
之后20年,他们再无任何联系,一个在南方,一个在北方,从此,真正的天各一方。这20年,女孩做生意,成了北方著名的画商,她在北京开了一家特别大的画廊,而且长期去国外买画卖画。不过,她还是一个人,虽然有很多追求的男子,可她就是微着摇头。
此时,女孩的母亲已经过世,弥留时拉着她的手说:“孩子,妈对不起你,耽误了你的一生。你去找他吧。”女孩哭了,这话,晚了20年,他已有妻有子,她还能去找他吗?
20年后,女孩已经是快50岁的人了,头发里有了银丝,额头上有了皱纹,她不再年轻,可是,她的心还是20多岁的样子,她的心里,还是他,全是他。
那天,接到电话时,女孩正在去俄罗斯谈生意的火车上,是一个陌生女人的电话。“我是他的妻子。”女人说,“他不行了,一直呼喊你的名字。我知道你,因为,他常常在梦中喊你的名字。”
刹那间,女孩崩溃了,浑身哆嗦着中途下车,然后赶往飞机场,她必须去见他,不管别人说什么,她都要去见他。春闺梦里相思又相思的人,你要等我啊!
看到对方的刹那,他们都呆了:少年子弟江湖老,红粉佳人两鬓斑啊!
在医院白被子里的男孩骨瘦如柴,面目早就全非——他得肝癌,晚期,如果不是等待她来,早就魂去他乡了。
“你怎么可以这样?谁让你变成这样的?……”女孩扑过去,满是委屈,“你说过要活动80岁,你就过你必须是我近旁的那棵树!”
男孩已经说不出话,只微微伸出手,想摸一下她的脸。她把脸埋在他的手心里,那手心里,有了一捧一捧的泪。
他的妻子、女儿站在旁边,泪如雨下。
几小时后,男孩离世。女孩心痛如死,去布置他的葬礼。他的寿衣,是她给他亲自穿上的,为他穿那件贴身衬衣时,她呆住了。他的胸口上有刺青,是一朵莲花,清秀无比。她泪如雨下,她的名字原本是青莲。
青莲,那是一朵刺青的莲花呀。
而她的刺青在心里,他的人、他的名字、他的容貌,全在她的心里,也是一道道刺青,一生无法抹掉。
葬礼之后,去男孩的家,女孩才知道,他过得那样清贫,做了一辈子中学教师,仍家徙四壁,妻子下了岗,女儿上大学没有钱,而他如果有钱,也不至于把病拖到这时候。他明明知道她有钱啊,她的消息在网上有多少啊,好多拍卖会都有她的身影,她一出手就是几千万啊,可是他居然没有张过口。这才是他呀!只是一棵朴素的树,远远地望着她,绝不纠缠她。
女孩做了让所有人都想不到的事情,给他妻子买了一栋当地最好的别墅,送他女儿出国留学,然后留下一大笔钱,悄然离去。
女孩明白,如果爱这个人,会爱他的所有——他的妻他的子,她都会爱。原来,爱到最后,全是心疼,全是怜悯,全是那一丝丝一缕缕剪不断理还乱的真情!
男孩走了,这世界显得那么空旷而无聊,根本来就是连在一起,盘根错节多少年!但现面,他走了,一个人去了另外一个世界。从此,女孩再也没有出现在在各种拍卖会上,再也没有锦衣玉貌地出现过。不久,她的葬礼在北京举行。她和他死在一年,相隔不到六个月。
女孩是忧郁而死的,她无儿无女。亲戚说,死时,她手里握着一枚玉,她枚玉叫双玉蝉。
是男孩的妻子埋葬了女孩,把她葬在他的身边,葬在了江南的那个小镇上。那是她向往了多少年的地方吧?
“让他们永远在一起吧,”男孩的妻子说,“坟前种上相思树,坟后种上同心花,让他们在天堂里相爱吧。”

文章转自[欣赏雨季] http://www.puresky.org