タスクはそれほど大きくはありませんが、常に発生するものであり、それを解決するために
ツールリンク
これで十分です。
問題の声明
/ resource / 1の代わりに/ resource / unique-resource-urlを介してアクセスするためのデータベーステーブルのエントリの一意のURLの自動生成。
降りる
ユーザーがサイト製品のリストを表示するときにどの地域/都市が選択されているかを簡単にナビゲートできるように、国と都市でサイトの検索を中断する必要があるとします。
新しいプロジェクトを作成することから始めましょう。
composer create-project laravel/laravel habr_url --prefer-dist
次に、 habr_urlのルートでcomposer.jsonを開き 、 必要なパッケージを追加します。
{ "name": "laravel/laravel", "description": "The Laravel Framework.", "keywords": ["framework", "laravel"], "license": "MIT", "require": { "laravel/framework": "4.1.*", "ivanlemeshev/laravel4-cyrillic-slug": "dev-master", "cviebrock/eloquent-sluggable": "1.0.*", "way/generators": "dev-master" }, "autoload": { "classmap": [ "app/commands", "app/controllers", "app/models", "app/database/migrations", "app/database/seeds", "app/tests/TestCase.php" ] }, "scripts": { "post-install-cmd": [ "php artisan optimize" ], "post-update-cmd": [ "php artisan clear-compiled", "php artisan optimize" ], "post-create-project-cmd": [ "php artisan key:generate" ] }, "config": { "preferred-install": "dist" }, "minimum-stability": "dev" }
"way/generators": "dev-master"
は、迅速なプロトタイプ作成のために追加します。
コンソールで
composer update
コマンドを実行し、パッケージが正常にインストールされた後、 app / config / app.phpに変更を加えます。
<?php return array( // ... 'providers' => array( // ... 'Ivanlemeshev\Laravel4CyrillicSlug\SlugServiceProvider', 'Cviebrock\EloquentSluggable\SluggableServiceProvider', 'Way\Generators\GeneratorsServiceProvider', ), // ... 'aliases' => array( // ... 'Slug' => 'Ivanlemeshev\Laravel4CyrillicSlug\Facades\Slug', 'Sluggable' => 'Cviebrock\EloquentSluggable\Facades\Sluggable', ), ); ?>
標準のStrクラスはラテン語でのみ機能するため、 Slugクラスを使用するとキリル文字からURLを生成できます。 Sluggableについては後ほど説明します。
コードを生成する
php artisan generate:scaffold create_countries_table --fields="name:string:unique, code:string[2]:unique" php artisan generate:scaffold create_cities_table --fields="name:string, slug:string:unique, country_id:integer:unsigned" php artisan generate:scaffold create_products_table --fields="name:string, slug:string:unique, price:integer, city_id:integer:unsigned"
外部キーを追加して新しいファイルを変更します。
// app/database/migrations/____create_cities_table.php class CreateCitiesTable extends Migration { // ... public function up() { Schema::create('cities', function(Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('slug')->unique(); $table->integer('country_id')->unsigned()->index(); $table->foreign('country_id')->references('id')->on('countries')->onDelete('cascade'); $table->timestamps(); }); } // ... }
// app/database/migrations/____create_products_table.php class CreateProductsTable extends Migration { // ... public function up() { Schema::create('products', function(Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('slug')->unique(); $table->integer('price'); $table->integer('city_id')->unsigned()->index(); $table->foreign('city_id')->references('id')->on('cities')->onDelete('cascade'); $table->timestamps(); }); } // ... }
また、
seeds
を通じてデータベースにいくつかの国と都市を追加します。 app / database / seedsフォルダーを開き、2つのファイルを変更します。
// app/database/seeds/CountriesTableSeeder.php class CountriesTableSeeder extends Seeder { public function run() { $countries = array( array('name' => '', 'code' => 'ru'), array('name' => '', 'code' => 'ua') ); // Uncomment the below to run the seeder DB::table('countries')->insert($countries); } }
// app/database/seeds/CitiesTableSeeder.php class CitiesTableSeeder extends Seeder { public function run() { // Uncomment the below to wipe the table clean before populating // DB::table('cities')->truncate(); $cities = array( array('name' => '', 'slug' => Slug::make(''), 'country_id' => 1), array('name' => '-', 'slug' => Slug::make('-'), 'country_id' => 1), array('name' => '', 'slug' => Slug::make(''), 'country_id' => 2), ); // Uncomment the below to run the seeder DB::table('cities')->insert($cities); } }
これは
Slug::make($input)
を使用します。これは
$input
を文字列として受け取り、そこから
moskva
または
sankt-peterburg
似たものを生成
sankt-peterburg
ます。
次に、データベース設定を変更します。
// app/config/database.php return array( // ... 'connections' => array( // ... 'mysql' => array( 'driver' => 'mysql', 'host' => 'localhost', 'database' => 'habr_url', 'username' => 'habr_url', 'password' => 'habr_url', 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', ), ), // ... );
そして、スキームとデータをデータベースに取り込みます。
php artisan migrate --seed
そして、これが私たちが得たものです:
リンクモデルに追加し、属性のルールを追加します。
// app/models/Product.php class Product extends Eloquent { protected $guarded = array(); public static $rules = array( 'name' => 'required|alpha_num|between:2,255', 'slug' => 'required|alpha_num|between:2,255|unique:products,slug', 'price' => 'required|numeric|between:2,255', 'city_id' => 'required|exists:cities,id' ); public function city() { return $this->belongsTo('City'); } }
// app/models/City.php class City extends Eloquent { protected $guarded = array(); public static $rules = array( 'name' => 'required|alpha_num|between:2,255', 'slug' => 'required|alpha_num|between:2,255|unique:cities,slug', 'country_id' => 'required|exists:countries,id' ); public function country() { return $this->belongsTo('Country'); } public function products() { return $this->hasMany('Product'); } }
// app/models/Country.php class Country extends Eloquent { protected $guarded = array(); public static $rules = array( 'name' => 'required|alpha_num|between:2,255|unique:countries,name', 'code' => 'required|alpha|size:2|unique:countries,code' ); public function cities() { return $this->hasMany('City'); } public function products() { return $this->hasManyThrough('Product', 'City'); } }
CitiesController
および
ProductsController
store
メソッドを
CitiesController
ます。
// app/models/CitiesController.php class CitiesController extends BaseController { // ... public function store() { $input = Input::all(); $input['slug'] = Slug::make(Input::get('name', '')); // ! $validation = Validator::make($input, City::$rules); if ($validation->passes()) { $this->product->create($input); return Redirect::route('products.index'); } return Redirect::route('products.create') ->withInput() ->withErrors($validation) ->with('message', 'There were validation errors.'); } // ... }
// app/models/ProductsController.php class ProductsController extends BaseController { // ... public function store() { $input = Input::all(); $input['slug'] = Slug::make(Input::get('name', '')); // ! $validation = Validator::make($input, Product::$rules); if ($validation->passes()) { $this->product->create($input); return Redirect::route('products.index'); } return Redirect::route('products.create') ->withInput() ->withErrors($validation) ->with('message', 'There were validation errors.'); } // ... }
アプリ/ビュー/都市/create.blade.php 、 アプリ/ビュー/都市/edit.blade.php 、 アプリ/ビュー/製品/create.blade.php 、 アプリ/ビュー/製品/edit.blade.phpから削除します対応するフォーム要素。
素晴らしい、
URL
生成され
URL
、それらが複製されるとどうなりますか? エラーが発生します。 そしてこれを避けるために-
slug
一致する場合、接頭辞を追加する必要があり、接頭辞がある場合はそれをインクリメントします。 多くの作業がありますが、優雅さはありません。 これらのジェスチャーを避けるために、
Eloquent Sluggable
を使用し
Eloquent Sluggable
。
まず、
Eloquent Sluggable
の構成をプロジェクトにドロップしましょう。
php artisan config:publish cviebrock/eloquent-sluggable
ここにある設定ファイルapp / config / cviebrock / eloquent-sluggable / config.phpで 、オプション
'method' => null
を
'method' => array('Slug', 'make')
ます。 したがって、キリル文字から音訳への変換と
URL
作成のタスクは、(キリル文字の操作方法を知らない標準のStrではなく) Slugクラスとそのmakeメソッドに割り当てられ
URL
。
このパッケージは何に適していますか? この原則に基づいて動作します。データベースにレコードを保存する
eloquent.saving*
イベントを想定し、生成された
slug
をモデル設定で指定されたフィールドに書き込みます。 設定例:
// app/models/City.php class City extends Eloquent { protected $guarded = array(); public static $rules = array( 'name' => 'required|alpha_num|between:2,255', 'country_id' => 'required|exists:countries,id' ); // public static $sluggable = array( 'build_from' => 'name', 'save_to' => 'slug', ); public function country() { return $this->belongsTo('Country'); } public function products() { return $this->hasMany('Product'); } }
既存の
slug
と一致する場合、接頭辞-1 、 -2などが新しいものに追加されます。 さらに、
slug
の
CitiesController@store
ルールを取り除き、
CitiesController@store
メソッドの
$input['slug'] = Slug::make(Input::get('name', ''));
という
CitiesController@store
$input['slug'] = Slug::make(Input::get('name', ''));
。
Product
についても同じことを行います。
// app/models/Product.php class Product extends Eloquent { protected $guarded = array(); public static $rules = array( 'name' => 'required|alpha_num|between:2,255', 'price' => 'required|numeric|between:2,255', 'city_id' => 'required|exists:cities,id' ); public static $sluggable = array( 'build_from' => 'name', 'save_to' => 'slug', ); public function city() { return $this->belongsTo('City'); } }
City
Modelで
$sluggable
を次のように書き換えると、この
slug
でさらに興味深いことができます。
// app/models/City.php class City extends Eloquent { protected $guarded = array(); public static $rules = array( 'name' => 'required|alpha_num|between:2,255', 'slug' => 'required|alpha_num|between:2,255|unique:cities,slug', 'country_id' => 'required|exists:countries,id' ); public static $sluggable = array( 'build_from' => 'name_with_country_code', 'save_to' => 'slug', ); public function country() { return $this->belongsTo('Country'); } public function products() { return $this->hasMany('Product'); } public function getNameWithCountryCodeAttribute() { return $this->country->code . ' ' . $this->name; } }
はい、オブジェクトから存在しないフィールドを選択し、ヘルパーとして追加できます。
CitiesTableSeeder
をわずかに変更して、目的の結果を達成します。
// app/database/seeds/CitiesTableSeeder.php class CitiesTableSeeder extends Seeder { public function run() { // Uncomment the below to wipe the table clean before populating // DB::table('cities')->truncate(); $cities = array( array('name' => '', 'country_id' => 1), array('name' => '-', 'country_id' => 1), array('name' => '', 'country_id' => 2), ); // Uncomment the below to run the seeder foreach ($cities as $city) { City::create($city); } } }
次に、移行をロールバックし、データとともに新しい移行を入力します。
php artisan migrate:refresh --seed
いくつかのルートを追加します。
// app/routes.php // ... Route::get('country/{code}', array('as' => 'country', function($code) { $country = Country::where('code', '=', $code)->firstOrFail(); return View::make('products', array('products' => $country->products)); })); Route::get('city/{slug}', array('as' => 'city', function($slug) { $city = City::where('slug', '=', $slug)->firstOrFail(); return View::make('products', array('products' => $city->products)); })); Route::get('product/{slug}', array('as' => 'product', function($slug) { $product = Product::where('slug', '=', $slug)->firstOrFail(); return View::make('product', compact('product')); }));
そして、いくつかのパターンを追加します。
<!-- app/views/nav.blade.php --> <ul class="nav nav-pills"> @foreach(Country::all() as $country) <li><a href="{{{ route('country', $country->code) }}}">{{{ $country->name }}}</a> @endforeach </ul>
<!-- app/views/products.blade.php --> @extends('layouts.scaffold') @section('main') @include('nav') <h1>Products</h1> @if ($products->count()) <table class="table table-striped table-bordered"> <thead> <tr> <th>Name</th> <th>Price</th> <th>City</th> </tr> </thead> <tbody> @foreach ($products as $product) <tr> <td><a href="{{{ route('product', $product->slug)}}}">{{{ $product->name }}}</a></td> <td>{{{ $product->price }}}</td> <td><a href="{{{ route('city', $product->city->slug) }}}">{{{ $product->city->name }}}</a></td> </tr> @endforeach </tbody> </table> @else There are no products @endif @stop
<!-- app/views/product.blade.php --> @extends('layouts.scaffold') @section('main') @include('nav') <h1>Product</h1> <table class="table table-striped table-bordered"> <thead> <tr> <th>Name</th> <th>Price</th> <th>City</th> </tr> </thead> <tbody> <tr> <td>{{{ $product->name }}}</td> <td>{{{ $product->price }}}</td> <td><a href="{{{ route('city', $product->city->slug) }}}">{{{ $product->city->name }}}</a></td> </tr> </tbody> </table> @stop
以上です。
デモとGit
PMでの通常のエラー。 提案と批判-コメントで。 ご清聴ありがとうございました。