ララノェル アプリケヌションのむンストヌル、構成、䜜成、デプロむ

だから、あなたはLaravelフレヌムワヌクを詊しおみるか、孊びたいず思っおいたす。

他のPHPフレヌムワヌクに粟通しおいる堎合-難しいこずではありたせんが、そうでない堎合-これは最初のフレヌムワヌクの優れた遞択肢です。

Laravel-職人のためのPHPフレヌムワヌク

蚘事は非垞に倧きいです。 週末に完党に読むこずをお勧めしたす。

怠け者の堎合
Github
アプリ



蚭眮


Laravelをむンストヌルするには、 Composerが必芁です
Composerは、 PHP䟝存関係管理ツヌルです。 プロゞェクトに必芁な䟝存ラむブラリを宣蚀し、プロゞェクトにむンストヌルできたす。
- 䜜曲家

環境は*nix環境にむンストヌルされたすサむトにはWindowsにむンストヌルするためのマニュアルがあり、さらにWAMPやGitなどのサヌバヌが必芁です。

非垞にクリヌンなOSがあるずしたす。 次に、タヌミナルを開き、 これらの行を入力しおコピヌしお貌り付けたす

 #    sudo apt-get update sudo apt-get install -y build-essential sudo apt-get install -y python-software-properties #    php 5.5 sudo add-apt-repository ppa:ondrej/php5 sudo apt-get update #   sudo apt-get install -y php5 sudo apt-get install -y apache2 sudo apt-get install -y libapache2-mod-php5 sudo apt-get install -y mysql-server sudo apt-get install -y php5-mysql sudo apt-get install -y php5-curl sudo apt-get install -y php5-gd sudo apt-get install -y php5-mcrypt sudo apt-get install -y git-core sudo apt-get install -y phpmyadmin #   phpmyadmin echo "Include /etc/phpmyadmin/apache.conf" | sudo tee -a /etc/apache2/apache2.conf #  mod_rewrite sudo a2enmod rewrite #  apache    sudo /etc/init.d/apache2 restart #   Composer curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer 

しばらくするず、必芁なツヌルがすべおむンストヌルされたす。
Laravelのむンストヌルに盎接進みたす。

 #     cd #    /home/%user% mkdir workspace #  workspace cd workspace #    mkdir php #   php cd php #    php 

habrフォルダヌにlaravelプロゞェクトを䜜成したす

 composer create-project laravel/laravel habr --prefer-dist # ....       .... 

䜜成されたプロゞェクトに移動し、 php artisan serveコマンドを実行しおすべおが機胜するこずを確認したしょう

 cd habr php artisan serve 

ロヌカルサヌバヌはhttp// localhost8000で利甚できたす。

念のため、 職人はLaravelいるコマンドラむンスクリプトです。 開発で䜿甚するための䟿利なコマンドが倚数甚意されおいたす。 Symfonyコン゜ヌルコンポヌネントの䞊で実行されたす。  Artisan CLI 。 コマンドラむンでさたざたな䟿利なものを䜜成できる䟿利なコマンドがたくさんありたす。 コマンドのリストをphp artisan listは、コマンドラむンでphp artisan listず入力したす。

アドレスhttp// localhost8000に移動するず、投皿の最初に矎しいスクリヌンセヌバヌが衚瀺されたす。

カスタマむズ


デヌタベヌス以降DBず呌びたすに接続するために、 Laravelにはapp / config /フォルダヌにある構成ファむルdatabase.phpがありたす。
たず、 MySQLデヌタベヌスずナヌザヌを䜜成しMySQL

 mysql -u root -p #    > CREATE DATABASE `habr` CHARACTER SET utf8 COLLATE utf8_general_ci; > CREATE USER 'habr'@'localhost' IDENTIFIED BY 'my_password'; > GRANT ALL PRIVILEGES ON habr.* TO 'habr'@'localhost'; > exit 

いいね MySQLにアクセスするためのすべおのデヌタがありMySQL 。パスワヌドmy_passwordを持぀ナヌザヌhabrず、 localhostホスト䞊のデヌタベヌスhabrです。 デヌタベヌス構成ファむルに移動しお、蚭定を倉曎したしょう。

Laravel DB蚭定ファむル

Laravelは、 移行ずスキヌムビルダヌずいう優れたツヌルがありたす。
移行は、デヌタベヌスのバヌゞョン管理の䞀皮です。 これにより、開発チヌムはデヌタベヌススキヌマを倉曎し、スキヌマの珟圚の状態を垞に曎新できたす。 通垞、移行ずスキヌマビルダヌを䜵甚するず、デヌタベヌススキヌマの管理が容易になりたす。
- 移行
Schema BuilderはSchemaクラスです。 デヌタベヌス内のテヌブルを操䜜するこずができたす。 Laravelサポヌトするすべおのデヌタベヌスでうたく機胜し、これらすべおのシステムに単䞀のAPIを備えおいたす。
- スキヌムビルダヌ

最初に、移行テヌブルを䜜成したす。

 php artisan migrate:install 

デヌタベヌス接続蚭定が正しい堎合、移行ずテヌブルを䜜成する準備ができおいたす。
しかし、その前に、Webアプリケヌションをより効率的か぀迅速に䜜成するために䜿甚できる远加パッケヌゞのむンストヌルを玹介したす。

Laravel 4ゞェネレヌタヌ

ゞェフリヌりェむの超䟿利なツヌルゞェネレヌタヌ 。 Github

次のような倚くの䟿利なコマンドを職人リストに远加したす。



パッケヌゞのむンストヌル

Composerを䜿甚したパッケヌゞのむンストヌルは非垞に簡単です。 アプリケヌションのルヌトにあるcomposer.jsonファむルを線集し、 "way/generators": "1.*"を"require"リストに远加"require"たす。

 "require": { "laravel/framework": "4.1.*", "way/generators": "1.*" }, 

その埌、プロゞェクトの䟝存関係を曎新する必芁がありたす。 タヌミナルに入る

 composer update 

最埌の仕䞊げは、 app / config / app.php構成ファむルのアプリケヌションプロバむダヌのリストぞの゚ントリです。

 'Way\Generators\GeneratorsServiceProvider' 

これで、 php artisanコマンドリストにも新しいgenerateコマンドが含たgenerateようになりたす。 次のセクションでは、 generateを䜿甚しおアプリケヌションを䜜成し、開発を高速化generate方法を瀺したす。

アプリケヌション䜜成


割匕付きのブログサむトを䜜成するずしたす。 これには次のものが必芁です。



デヌタベヌスのテヌブルスキヌマの抂芁を説明したす。 私はこのようなものを埗たした
初期DBスキヌマ

このゞェネレヌタヌ 'yに感謝したす。 ちなみに、10行を登録するだけなので、次のずおりです。

 php artisan generate:migration create_users_table --fields="email:string:unique, password:string[60], username:string:unique, remember_token:string:nullable" php artisan generate:scaffold role --fields="role:string:unique" php artisan generate:pivot users roles php artisan generate:scaffold city --fields="name:string:unique" php artisan generate:scaffold company --fields="title:string:unique" php artisan generate:scaffold tag --fields="title:string:unique" php artisan generate:scaffold offer --fields="title:string, description:text, city_id:integer:unsigned, company_id:integer:unsigned, off:integer:unsigned, image:string, expires:date" php artisan generate:scaffold comment --fields="body:text, user_id:integer:unsigned, offer_id:integer:unsigned, mark:integer" php artisan generate:pivot offers tags #      php artisan migrate 

最埌のコマンドを䜿甚するず、ただ蚘録されおいないすべおの移行がデヌタベヌスに入力されたす。 重芁なこずは、すべおの新しい移行が1぀のスタックによっお起動されるこずです。 マむグレヌションをロヌルバックするために、 php artisan migrate:rollbackコマンドがあり、すべおのマむグレヌションをれロにロヌルバックするには、 migrate:reset 、れロにロヌルするには、すべおのmigrate:refreshマむグレヌションを実行したす。

4.1.25以降の Laravelバヌゞョンでは、盗たれたCookieで穎を塞ぐセキュリティアップデヌトがありたした。 アップデヌトず手順の詳现に぀いおは、 http  //laravel.com/docs/upgradeをご芧ください 。バヌゞョンがLaravel < 4.1.26の堎合 。 たたは、 vlom88 http://habrahabr.ru/post/197454/#comment_7510479からコメントを読んで ください 。


ゞェネレヌタヌコマンドの詳现



このゞェネレヌタヌの䜿甚䟋が、その䜿甚方法ずその有甚性を明確に瀺しおいるこずを願っおいたす。

ただ䞍足しおいるのは、テヌブル間のリンクです。
知っおおくこずが重芁です テヌブルの列に倖郚キヌを远加する堎合、列が眲名されおいないこずを確認する必芁がありたす。

さお、それらを远加したす

 php artisan generate:migration add_foreign_user_id_and_offer_id_to_comments_table php artisan generate:migration add_foreign_city_id_and_company_id_to_offers_table 

このような倉曎は自動的に䜜成されないため、移行ファむル自䜓にむンデックスの远加を登録する必芁がありたす。

 ... class AddForeignUserIdAndOfferIdToCommentsTable extends Migration { ... public function up() { Schema::table('comments', function(Blueprint $table) { $table->index('user_id'); $table->index('offer_id'); $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); $table->foreign('offer_id')->references('id')->on('offers')->onDelete('cascade'); }); } ... public function down() { Schema::table('comments', function(Blueprint $table) { $table->dropForeign('comments_user_id_foreign'); $table->dropForeign('comments_offer_id_foreign'); $table->dropIndex('comments_user_id_index'); $table->dropIndex('comments_offer_id_index'); }); } } ... class AddForeignCityIdAndCompanyIdToOffersTable extends Migration { ... public function up() { Schema::table('offers', function(Blueprint $table) { $table->index('city_id'); $table->index('company_id'); $table->foreign('city_id')->references('id')->on('cities')->onDelete('cascade'); $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade'); }); } ... public function down() { Schema::table('offers', function(Blueprint $table) { $table->dropForeign('offers_city_id_foreign'); $table->dropForeign('offers_company_id_foreign'); $table->dropIndex('offers_city_id_index'); $table->dropIndex('offers_company_id_index'); }); } } 

デヌタベヌススキヌマを芋るず、より良い状況がわかりたす。
クヌルなDBスキヌマ

珟時点では、リ゜ヌスぞのリンクはすべお開いおおり、誰にでも枡すこずができたす。
adminロヌルを远加するずしたす。 リンクhttp// localhost8000 / rolesで 、次の図が衚瀺されたす。
管理者ロヌルが远加されたした

LaravelのテンプレヌトずBladeテンプレヌト゚ンゞンに぀いお少し。
テンプレヌトファむルの堎合、拡匵子.blade.phpが䜿甚されたす。 ファむルアプリ/ビュヌ/レむアりト/ scaffold.blade.phpを芋るず、

 // app/views/layouts/scaffold.blade.php <!doctype html> <html> <head> <meta charset="utf-8"> <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet"> <style> table form { margin-bottom: 0; } form ul { margin-left: 0; list-style: none; } .error { color: red; font-style: italic; } body { padding-top: 20px; } </style> </head> <body> <div class="container"> @if (Session::has('message')) <div class="flash alert"> <p>{{ Session::get('message') }}</p> </div> @endif @yield('main') </div> </body> </html> 

ここで䜕が起こっおいたすか ファむル自䜓はスケルトンであり、コンテンツたたは別のテンプレヌトをmainセクションに远加するこずで拡匵できるレむアりトです。 二重䞭括匧{{$ var}}は<Php echo $ var;に類䌌しおいたす。 > ここでSessionクラスは、䜕らかのメッセヌゞを枡す堎合にナヌザヌにメッセヌゞを衚瀺するために䜿甚されたす。 メッセヌゞは䞀時的なものであり、ペヌゞが曎新されるず消えたす。 新しく䜜成したテンプレヌトアプリ/ビュヌ/ロヌル/index.blade.phpを開くず

 // app/views/roles/index.blade.php @extends('layouts.scaffold') @section('main') <h1>All Roles</h1> <p>{{ link_to_route('roles.create', 'Add new role') }}</p> @if ($roles->count()) <table class="table table-striped table-bordered"> <thead> <tr> <th>Role</th> </tr> </thead> <tbody> @foreach ($roles as $role) <tr> <td>{{{ $role->role }}}</td> <td>{{ link_to_route('roles.edit', 'Edit', array($role->id), array('class' => 'btn btn-info')) }}</td> <td> {{ Form::open(array('method' => 'DELETE', 'route' => array('roles.destroy', $role->id))) }} {{ Form::submit('Delete', array('class' => 'btn btn-danger')) }} {{ Form::close() }} </td> </tr> @endforeach </tbody> </table> @else There are no roles @endif @stop 

このテンプレヌトがapp / views / layouts / scaffold.blade.phpテンプレヌトを@extends('layouts.scaffold') 、 @extends('layouts.scaffold')コヌドがこのこずを瀺しおいるこずが明らかになりたす 。 ここでは、 /を䜿甚するこずもできたすが、フォルダヌを区切るためにドットが䜿甚されおいるこずに泚意しおください。

その埌、 @stopが最初に珟れるたで、すべおがmainセクションに曞き蟌たれたす。 たた、おなじみのif - else - endifおよびforeach - endforeach 、ヘルパヌ関数link_to_route 、Laravelヘルパヌ関数およびFormクラスがForm䜜成に提䟛するものを䜿甚したす少なくずもForm :: openを䜿甚する必芁がありたす _token圢匏の远加属性の䜜成方法-PUT / PATCHたたはDELETEの堎合のクロスサむトリク゚ストフォヌゞェリおよび_methodに察する保護。

たず、すべおのリ゜ヌスを保護するこずに぀いお考えおみたしょう。 これを行うには、承認を入力する必芁がありたす。

app / controllersフォルダヌに新しいLoginContollerを䜜成したす

 php artisan generate:controller LoginController 

そしお、いく぀かのテンプレヌトを远加したす

 mkdir app/views/login php artisan generate:view index --path="app/views/login" php artisan generate:view register --path="app/views/login" php artisan generate:view dashboard --path="app/views/login" 

それでは、コントロヌラヌ自䜓を倉曎したしょう。 5぀のメ゜ッドが必芁です。

倉曎されたLoginControllerは次のようになりたす。

 // app/controllers/LoginController.php class LoginController extends BaseController { /** * Login Form. * * @return Response */ public function index() { return View::make('login.index'); } /** * Registration form. * * @return Response */ public function register() { return View::make('login.register'); } /** * Registring new user and storing him to DB. * * @return Response */ public function store() { $rules = array( 'email' => 'required|email|unique:users,email', 'password' => 'required|alpha_num|between:4,50', 'username' => 'required|alpha_num|between:2,20|unique:users,username' ); $validator = Validator::make(Input::all(), $rules); if($validator->fails()){ return Redirect::back()->withInput()->withErrors($validator); } $user = new User; $user->email = Input::get('email'); $user->username = Input::get('username'); $user->password = Hash::make(Input::get('password')); $user->save(); Auth::loginUsingId($user->id); return Redirect::home()->with('message', 'Thank you for registration, now you can comment on offers!'); } /** * Log in to site. * * @return Response */ public function login() { if (Auth::attempt(array('email' => Input::get('email'), 'password' => Input::get('password')), true) || Auth::attempt(array('username' => Input::get('email'), 'password' => Input::get('password')), true)) { return Redirect::intended('dashboard'); } return Redirect::back()->withInput(Input::except('password'))->with('message', 'Wrong creadentials!'); } /** * Log out from site. * * @return Response */ public function logout() { Auth::logout(); return Redirect::home()->with('message', 'See you again!'); } } 

最初の2぀のメ゜ッドは、HTMLテンプレヌトから生成されたす。
storeメ゜ッドは新しいナヌザヌをデヌタベヌスに保存し、 POST介しおInput::all()れるInput::all()からのすべおのデヌタを受け入れたす。  詳现 。
Inputクラスには、POST芁求䞭に送信されたデヌタが含たれたす。 all() 、 get() 、 has()およびその他 Basic Input などの静的メ゜ッドが倚数ありたす。

Hashは、デヌタベヌス内のパスワヌドが暗号化された圢匏で保存されるように、 bcryptメ゜ッドを䜿甚する暗号化クラスです Laravel Security 。

ただし、登録する前に、受信デヌタを怜蚌する必芁がありたす。
このため、 LaravelにはValidatorクラスがありたす。 Validation::makeメ゜ッドは、2぀たたは3぀の匕数を取りたす。
  1. $input必須、チェックする入力の配列
  2. $rules -必須、受信デヌタのルヌルを含む配列
  3. $messagesオプション、゚ラヌメッセヌゞを含む配列

利甚可胜なルヌルの完党なリストは、利甚可胜な怜蚌ルヌルにありたす。

fails()メ゜ッドは、 makeメ゜ッドに枡したルヌルに埓っおデヌタが怜蚌されたかどうかに応じお、 trueたたはfalseを返したす 。

Redirectクラスは、リダむレクトに䜿甚されたす。 圌の方法


Authクラスは承認クラスであり、デヌタベヌスから指定された識別子 Authenticating Users によっおナヌザヌを承認するloginUsingId($id)など、いく぀かのメ゜ッドがありたす。 登録埌、ナヌザヌを自動的に承認するため、䜿甚したす。

Controller login()のメ゜ッドは、ナヌザヌをemailたたはusernameで認蚌し、認蚌フィルタヌの䞋にあるペヌゞにリダむレクトしたす。 デヌタが䞀臎しない堎合、受信デヌタ、゚ラヌメッセヌゞ、パスワヌドなしでリダむレクトしたす。

そのため、承認を担圓するコントロヌラヌがありたす。

すべおのリ゜ヌスをアクセスから隠すための次のステップは、アプリケヌションのルヌトを含むapp / routes.phpファむルを倉曎するこずです 。

 // app/routes.php ... Route::get('/', array('as' => 'home', function() { return View::make('hello'); })); Route::get('logout', array('as' => 'login.logout', 'uses' => 'LoginController@logout')); Route::group(array('before' => 'un_auth'), function() { Route::get('login', array('as' => 'login.index', 'uses' => 'LoginController@index')); Route::get('register', array('as' => 'login.register', 'uses' => 'LoginController@register')); Route::post('login', array('uses' => 'LoginController@login')); Route::post('register', array('uses' => 'LoginController@store')); }); Route::group(array('before' => 'admin.auth'), function() { Route::get('dashboard', function() { return View::make('login.dashboard'); }); Route::resource('roles', 'RolesController'); Route::resource('cities', 'CitiesController'); Route::resource('companies', 'CompaniesController'); Route::resource('tags', 'TagsController'); Route::resource('offers', 'OffersController'); Route::resource('comments', 'CommentsController'); }); Route::filter('admin.auth', function() { if (Auth::guest()) { return Redirect::to('login'); } }); Route::filter('un_auth', function() { if (!Auth::guest()) { Auth::logout(); } }); 

/ロヌルなどのリンクをたどるず、 / loginペヌゞにリダむレクトされたす"index.blade.php"ペヌゞには、これたで暙準テキスト"index.blade.php"のみが衚瀺されたす。

Route::group(array('before' => 'admin.auth'))で囲たれたすべおのルヌトに察しお、 admin.authフィルタヌが適甚され、ナヌザヌがゲストであるかどうか、および-ログむンペヌゞに送信したす。 ここでフィルタに぀いお、そしおルヌトのグルヌプ化に぀いおここで読むこずができたす 。 別のフィルタヌRoute::group(array('before' => 'un_auth'))は、ナヌザヌがサむトにログオンしおいるかどうかを確認し、怜蚌が完了したらログオフしたす。

通垞の操䜜では、ログむンファむルず登録ファむルを倉曎したす。

 // app/views/login/index.blade.php @extends('layouts.scaffold') @section('main') <h1>Login</h1> <p>{{ link_to_route('login.register', 'Register') }}</p> {{ Form::open(array('route' => 'login.index')) }} <ul> <li> {{ Form::label('email', 'Email or Username:') }} {{ Form::text('email') }} </li> <li> {{ Form::label('password', 'Password:') }} {{ Form::password('password') }} </li> <li> {{ Form::submit('Submit', array('class' => 'btn btn-info')) }} </li> </ul> {{ Form::close() }} @include('partials.errors', $errors) @stop // app/views/login/register.blade.php @extends('layouts.scaffold') @section('main') <h1>Register</h1> <p>{{ link_to_route('login.index', 'Login') }}</p> {{ Form::open(array('route' => 'login.register')) }} <ul> <li> {{ Form::label('email', 'Email:') }} {{ Form::text('email') }} </li> <li> {{ Form::label('username', 'Username:') }} {{ Form::text('username') }} </li> <li> {{ Form::label('password', 'Password:') }} {{ Form::password('password') }} </li> <li> {{ Form::submit('Submit', array('class' => 'btn btn-info')) }} </li> </ul> {{ Form::close() }} @include('partials.errors', $errors) @stop // app/views/login/dashboard.blade.php @extends('layouts.scaffold') @section('main') <h1>Administrative Dashboard</h1> <p>Nice to see you, <b>{{{ Auth::user()->username }}}</b></p> @stop // app/views/partials/errors.blade.php @if ($errors->any()) <ul> {{ implode('', $errors->all('<li class="error">:message</li>')) }} </ul> @endif 

お気づきのように、ここでは@include('view', $variable)テンプレヌト@include('view', $variable)新しいトリックを䜿甚したした。 アプリケヌションでは、非垞に簡単です-2぀の匕数を枡したす。
  1. view-特定のテンプレヌトに含たれるテンプレヌト
  2. $ variable-テンプレヌトをレンダリングするために枡す必芁がある倉数

サむトに登録しお、サむトにアクセスしたす。

さお、今あなたはリ゜ヌスを行うこずができたす。郜垂から始めたしょう。たず、ModelのCity怜蚌ルヌルを倉曎したす

 // app/models/City.php class City extends Eloquent { protected $guarded = array(); public static $rules = array( 'name' => 'required|alpha|min:2|max:200|unique:cities,name' ); } 

それは同様にしおモデル怜蚌ルヌルを倉曎した埌だCompany、RoleずTag

 // app/models/Company.php ... public static $rules = array( 'name' => 'required|alpha|min:2|max:200|unique:companies,name' ); ... // app/models/Role.php ... public static $rules = array( 'role' => 'required|alpha|min:2|max:200|unique:roles,role' ); ... // app/models/Tag.php ... public static $rules = array( 'name' => 'required|min:2|max:200|unique:tags,name' ); ... 

リンクの切り替えの利䟿性のために、app / views / layouts / scaffold.blade.phpにメニュヌを远加し、将来のニヌズに合わせおjQueryずjQuery-UIを远加したす

 // app/views/layouts/scaffold.blade.php <!doctype html> <html> <head> <meta charset="utf-8"> <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet"> <link href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" rel="stylesheet"> <style> table form { margin-bottom: 0; } form ul { margin-left: 0; list-style: none; } .error { color: red; font-style: italic; } body { padding-top: 20px; } input, textarea, .uneditable-input {width: 50%; min-width: 200px;} </style> @yield('styles') </head> <body> <div class="container"> <ul class="nav nav-pills"> <li>{{ link_to_route('offers.index', 'Offers') }}</li> <li>{{ link_to_route('tags.index', 'Tags') }}</li> <li>{{ link_to_route('roles.index', 'Roles') }}</li> <li>{{ link_to_route('cities.index', 'Cities') }}</li> <li>{{ link_to_route('comments.index', 'Comments') }}</li> <li>{{ link_to_route('companies.index', 'Companies') }}</li> <li class="pull-right">{{ link_to_route('login.logout', 'Logout') }}</li> </ul> @if (Session::has('message')) <div class="flash alert"> <p>{{ Session::get('message') }}</p> </div> @endif @yield('main') </div> <script type="text/javascript" src="//code.jquery.com/jquery.min.js"></script> <script type="text/javascript" src="//code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script> @yield('scripts') </body> </html> 

次に、モデルの怜蚌ルヌルを線集したすOffer。

 // app/models/Offer.php ... public static $rules = array( 'title' => 'required|between:5,200', 'description' => 'required|min:10', 'city_id' => 'required|exists:cities,id', 'company_id' => 'required|exists:companies,id', 'off' => 'required|numeric|min:1|max:100', 'image' => 'required|regex:/\/images\/\d{4}\/\d{2}\/\d{2}\/([A-z0-9]){30}\.jpg/', // matches /images/2012/12/21/ThisIsTheEndOfTheWorldMaya2112.jpg 'expires' => 'required|date' ); 

ここでは、写真をアップロヌドimageする手段AJAXを䜿甚し、サヌバヌ䞊の写真ぞのパスのみを怜蚌自䜓に転送する必芁があるため、フィヌルドに耇雑なパタヌンを䜿甚したした。それでは、テンプレヌトアプリ/ views / offers / create.blade.phpを倉曎し、スクリプト甚の別のファむルを䜜成するこずから始めたしょう。

 // app/views/offers/create.blade.php ... {{ Form::label('file', 'Image:') }} {{ Form::file('file')}} <img src="" id="thumb" style="max-width:300px; max-height: 200px; display: block;"> {{ Form::hidden('image') }} <div class="error"></div> ... @section('scripts') @include('offers.scripts') @stop // app/views/offers/scripts.blade.php <script> $(document).ready(function(){ //     $('#expires').datepicker({dateFormat: "yy-mm-dd"}); var uploadInput = $('#file'), //    imageInput = $('[name="image"]'), //   URL  thumb = document.getElementById('thumb'), //   error = $('div.error'); //      uploadInput.on('change', function(){ //     FormData var data = new FormData(); //      data.append('file', uploadInput[0].files[0]); //    $.ajax({ //   URL    url: '/upload', //   type: 'POST', //     data: data, //     jQuery   processData: false, //     jQuery    contentType: false, //      dataType: 'json', //      success: function(result) { //     (    result) //      filelink if (result.filelink) { //   URL    thumb.setAttribute('src', result.filelink); //    input' imageInput.val(result.filelink); //   error.hide(); } else { //      error.text(result.message); error.show(); } }, // -    error: function (result) { //     error.text("Upload impossible"); error.show(); } }); }); }); </script> 

ここでは、抌しお画像を远加するinput[name="file"]ず、䜿甚しおそれを送信するAJAXにはURL/アップロヌド。このURLからの応答は、ダりンロヌドした画像ぞのリンクになりたす。このリンクを#thumb画像のsrc属性に挿入し、非衚瀺の入力に保存したすimage。次に、app / routes.phpファむルにmarshout を远加する必芁がありたすupload。

 // app/routes.php ... Route::group(array('before' => 'admin.auth'), function(){ ... Route::resource('comments', 'CommentsController'); Route::post('upload', array('uses' => 'HomeController@uploadOfferImage')); } ... 

さお、URLを登録したしたHomeController。ロゞックをに登録するのは残りたす。これを行うには、app / controllers / HomeController.phpファむルにuploadOfferImage
min メ゜ッドを远加したす
 // app/controllers/HomeController.php class HomeController extends BaseController { ... public function uploadOfferImage() { $rules = array('file' => 'mimes:jpeg,png'); $validator = Validator::make(Input::all(), $rules); if ($validator->fails()) { return Response::json(array('message' => $validator->messages()->first('file'))); } $dir = '/images'.date('/Y/m/d/'); do { $filename = str_random(30).'.jpg'; } while (File::exists(public_path().$dir.$filename)); Input::file('file')->move(public_path().$dir, $filename); return Response::json(array('filelink' => $dir.$filename)); } } 

ルヌル、怜蚌、゚ラヌ、回答など、すべおが非垞に簡単です。最初に保存するために、保存するフォルダを蚭定したす-これはpublic_path/ images /珟圚の幎/月/日付/public_path()これはLaravelパブリックファむルぞのパスの補助関数です、そしおstr_random(30)30文字のランダムなファむル名を䜜成し、拡匵子jpg。その埌、クラスInputずそのメ゜ッドを䜿甚しfile('file')->move('destination_path', 'filename')たす。「file」は着信ファむル、「destination_path」はファむルの移動先フォルダヌ、「filename」は保存するファむルの名前です。
Response::json圢匏で答えを䞎えるでしょうjson。
いいねファむルはで読み蟌たれAJAXたす。
AJAX upload Laravel
次のステップは倉曎になるForm::input('number', 'city_id')ず、Form::input('number', 'company_id')実際のデヌタずSELECTAで。

 // app/views/offers/create.blade.php ... <?php $cities = array(0 => 'Choose city'); foreach (City::get(array('id', 'name')) as $city) { $cities[$city->id] = $city->name; } ?> <li> {{ Form::label('city_id', 'City_id:') }} {{ Form::select('city_id', $cities) }} </li> <?php $companies = array(0 => 'Choose company'); foreach (Company::get(array('id', 'name')) as $company) { $companies[$company->id] = $company->name; } ?> <li> {{ Form::label('company_id', 'Company_id:') }} {{ Form::select('company_id', $companies) }} </li> ... 

遞択がどのように機胜するかは、フォヌムずHTMLドロップダりンリストで確認できたす。したがっお、デヌタベヌス内の既存の郜垂や䌁業から遞択できたす。

ただ䞍足しおいるのは、割匕にタグを远加するこずです。ここで、オヌトコンプリヌトを備えたjquery-uiは、耇数の倀を远加するのに圹立ちたす。これを行うには、スクリプトapp / views / offers / create.blade.phpでファむルを展開したす

 // app/views/offers/scripts.blade.php <script> $(document).ready(function(){ ... function split( val ) { return val.split( /,\s*/ ); } function extractLast( term ) { return split( term ).pop(); } $( "#tags" ) // don't navigate away from the field on tab when selecting an item .bind( "keydown", function( event ) { if ( event.keyCode === $.ui.keyCode.TAB && $( this ).data( "ui-autocomplete" ).menu.active ) { event.preventDefault(); } }) .autocomplete({ source: function( request, response ) { $.getJSON( "/tags", { term: extractLast( request.term ), }, function( data ) { response($.map(data, function(item) { return { value: item.name } })) } ); }, search: function() { // custom minLength var term = extractLast( this.value ); if ( term.length < 2 ) { return false; } }, focus: function() { // prevent value inserted on focus return false; }, select: function( event, ui ) { console.log(ui); console.log(this); var terms = split( this.value ); // remove the current input terms.pop(); // add the selected item terms.push( ui.item.value ); // add placeholder to get the comma-and-space at the end terms.push( "" ); this.value = terms.join( ", " ); return false; } }); }); </script> 

これはjqueryui.comの暙準的な䜿甚䟋であり、サヌバヌからの応答ポむントでわずかに倉曎されおいたす。ご芧のずおり、アドレスはに移動し/tagsたす。私たちはこれのAJAX芁求ぞの応答の論理を組織しURLたす。

 // app/controllers/TagController.php class TagsController extends BaseController { ... /** * Display a listing of the resource. * * @return Response */ public function index() { $tags = $this->tag->all(); //   AJAX  if (Request::ajax()) { //    ,      $tags = Tag::where('name', 'like', '%'.Input::get('term', '').'%')->get(array('name')); //     json return $tags; } return View::make('tags.index', compact('tags')); } ... 

おもしろいのは、返されるずEloquentフォヌマットに倉換されるため、json䜿甚する必芁がないこずResponse::json()です。そしお、ここにオヌトコンプリヌトタグがありたす。

最埌に行う必芁があるのは、割匕を䜜成するためのロゞックを倉曎するこずです。
 // app/controllers/OffersController.php class OffersController extends BaseController { ... /** * Store a newly created resource in storage. * * @return Response */ public function store() { $rules = Offer::$rules; $rules['expires'] .= '|after:'.date('Ym-d', strtotime('+1 day')).'|before:'.date('Ym-d', strtotime('+1 month')); $validation = Validator::make(Input::all(), $rules); if ($validation->passes()) { $tags = array(); foreach (explode(', ', Input::get('tags')) as $tag_name) { if ($tag = Tag::where('name', '=', $tag_name)->first()) { $tags[] = $tag->id; } } if (count($tags) == 0) { return Redirect::route('offers.create') ->withInput() ->with('message', 'Insert at least one tag.'); } $offer = $this->offer->create(Input::except('tags', 'file')); $offer->tags()->sync($tags); return Redirect::route('offers.index'); } return Redirect::route('offers.create') ->withInput() ->withErrors($validation) ->with('message', 'There were validation errors.'); } ... 

最初に、割匕が明日より早くなく、1か月埌に遅く終了するように、期限切れルヌルを拡匵したす。次に、id別の配列内のすべおのタグを遞択し、デヌタベヌス内のタグの存圚を確認したす。次に、タグが入力されおいるかどうかを確認する小さなチェックがありたす。そしお最埌に、非垞に興味深いトリックEloquentでは、テヌブルをリンクするために異なるリレヌションEloquent Relationshipsを䜿甚できたす。たずえば、Offersモデルには倚くのタグを含めるこずができ、それに応じおModelに蚘述したす

 // app/models/Offer.php ... public function tags() { return $this->belongsToMany('Tag'); } ... 

したがっお、offersテヌブルの1぀のレコヌドずtagsテヌブルの倚くのレコヌドの間に関係を䜜成したした。メ゜ッドを参照する$offer->tags()ず、特定の割匕に関連付けられおいるすべおのタグを取埗できたす。ただし、この䟋では、䞭間テヌブルを操䜜するための特別なメ゜ッドを䜿甚しおいsync(array(1, 2, 3))たす。これは、䞭間テヌブルのoffer_id必芁なテヌブルに曞き蟌みたすtag_id。衚offer_tag
Pivot table offer to tag
我々はたた、テヌブル内のレコヌド間のリンクを指定する必芁が申し出テヌブルでレコヌドの郜垂ず䌁業

 // app/models/Offer.php ... public function city() { return $this->belongsTo('City'); } public function company() { return $this->belongsTo('Company'); } public function tags() { return $this->belongsToMany('Tag'); } //         +     public function webDescription($options = array()) { $str = $this->description; if (isset($options['shorten'])) { $length = isset($options['length']) ? (int) $options['length'] : 250; $end = isset($options['end']) ? : '
'; if (mb_strlen($str) > $length) { $str = mb_substr(trim($str), 0, $length); $str = mb_substr($str, 0, mb_strlen($str) - mb_strpos(strrev($str), ' ')); $str = trim($str.$end); } } $str = str_replace("\r\n", '<br>', e($str)); return $str; } } 

ファむルアプリ/ビュヌ/オファヌ/index.blade.phpを倉曎するために残っおいたす

 // app/views/offers/index.blade.php @if ($offers->count()) <table class="table table-striped table-bordered"> <thead> <tr> <th>Title</th> <th>Description</th> <th>City</th> <th>Company</th> <th>Off</th> <th>Image</th> <th>Tags</th> <th>Expires</th> </tr> </thead> <tbody> @foreach ($offers as $offer) <tr> <td>{{{ $offer->title }}}</td> <td>{{ $offer->webDescription(array('shorten' => true, 'length' => 60)) }}</td> <td>{{{ $offer->city->name }}}</td> <td>{{{ $offer->company->name }}}</td> <td>{{{ $offer->off }}}</td> <td><img src="" style="max-width: 200px; max-height:150px;"></td> <td> @foreach($offer->tags as $tag) <span class="badge">{{{$tag->name}}}</span> @endforeach </td> <td>{{{ $offer->expires }}}</td> <td> {{ link_to_route('offers.edit', 'Edit', array($offer->id), array('class' => 'btn btn-info')) }} </td> <td> {{ Form::open(array('method' => 'DELETE', 'route' => array('offers.destroy', $offer->id))) }} {{ Form::submit('Delete', array('class' => 'btn btn-danger')) }} {{ Form::close() }} </td> </tr> @endforeach </tbody> </table> @else There are no offers @endif 

そしお、割匕の構造を完党に反映した玠晎らしい写真を芋るこずができたす。
All offers
{{{ $string }}} $string , htmlentities , , XSS. <?php echo htmlentities($string); ?> Laravel e($string)


app/views/offers/edit.blade.php , app/views/offers/show.blade.php update app/controllers/OfferController.php .

app/views/edit.blade.php
 // app/views/offers/edit.blade.php @extends('layouts.scaffold') @section('main') <h1>Edit Offer</h1> {{ Form::model($offer, array('method' => 'PATCH', 'route' => array('offers.update', $offer->id))) }} <ul> <li> {{ Form::label('title', 'Title:') }} {{ Form::text('title') }} </li> <li> {{ Form::label('description', 'Description:') }} {{ Form::textarea('description') }} </li> <?php $cities = array(0 => 'Choose city'); foreach (City::get(array('id', 'name')) as $city) { $cities[$city->id] = $city->name; } ?> <li> {{ Form::label('city_id', 'City_id:') }} {{ Form::select('city_id', $cities) }} </li> <?php $companies = array(0 => 'Choose company'); foreach (Company::get(array('id', 'name')) as $company) { $companies[$company->id] = $company->name; } ?> <li> {{ Form::label('company_id', 'Company_id:') }} {{ Form::select('company_id', $companies) }} </li> <li> {{ Form::label('off', 'Off:') }} {{ Form::input('number', 'off') }} </li> <li> {{ Form::label('file', 'Image:') }} {{ Form::file('file')}} <img src="" id="thumb" style="max-width:300px; max-height: 200px; display:block; "> {{ Form::hidden('image') }} <div class="error"></div> </li> <li> {{ Form::label('expires', 'Expires:') }} {{ Form::text('expires') }} </li> <li> {{ Form::label('tags', 'Tags:') }} {{ Form::text('tags', Input::old('tags', implode(', ', array_fetch($offer->tags()->get(array('name'))->toArray(), 'name')))) }} </li> <li> {{ Form::submit('Update', array('class' => 'btn btn-info')) }} {{ link_to_route('offers.show', 'Cancel', $offer->id, array('class' => 'btn')) }} </li> </ul> {{ Form::close() }} @if ($errors->any()) <ul> {{ implode('', $errors->all('<li class="error">:message</li>')) }} </ul> @endif @stop @section('scripts') @include('offers.scripts') @stop 

app/views/offers/create.blade.php , {{ Form::text('tags', ... }} . : — , — image . Form::text('tags', ... ) , , , $offer->tags() name . Laravel array_fetch , , , .

update OfferController :

 // app/controllers/OfferController.php class OffersController extends BaseController { ... public function update($id) { $offer = $this->offer->findOrFail($id); $rules = Offer::$rules; $rules['expires'] .= '|after:'.date('Ym-d', strtotime('+1 day')).'|before:'.date('Ym-d', strtotime('+1 month')); $validation = Validator::make(Input::all(), $rules); if ($validation->passes()) { $tags = array(); foreach (explode(', ', Input::get('tags')) as $tag_name) { if ($tag = Tag::where('name', '=', $tag_name)->first()) { $tags[] = $tag->id; } } if (count($tags) == 0) { return Redirect::route('offers.create') ->withInput() ->withErrors($validation) ->with('message', 'Insert at least one tag.'); } $offer->update(Input::except('tags', 'file', '_method')); $offer->tags()->sync($tags); return Redirect::route('offers.show', $id); } return Redirect::route('offers.edit', $id) ->withInput() ->withErrors($validation) ->with('message', 'There were validation errors.'); } ... 

addメ゜ッドずの違いは最小限です。間違ったものが指定された堎合、最初に404゚ラヌをスロヌし、id次にメ゜ッドを䜿甚しupdate($id)たす。それがすべおの倉曎です。

次に、ファむルapp / views / offers / show.blade.phpを倉曎したす

 // app/views/offers/show.blade.php ... <thead> <tr> <th>Title</th> <th>Description</th> <th>City_id</th> <th>Company_id</th> <th>Off</th> <th>Image</th> <th>Tags</th> <th>Expires</th> </tr> </thead> <tbody> <tr> <td>{{{ $offer->title }}}</td> <td>{{ $offer->webDescription(array('shorten' => true, 'length' => 60)) }}</td> <td>{{{ $offer->city->name }}}</td> <td>{{{ $offer->company->name }}}</td> <td>{{{ $offer->off }}}</td> <td><img src="" style="max-width: 200px; max-height:150px;"/></td> <td> @foreach($offer->tags as $tag) <span class="badge">{{{ $tag->name }}}</span> @endforeach </td> <td>{{{ $offer->expires }}}</td> ... 

今、割匕を倉曎した埌、画像ずすべおのリレヌショナルデヌタでその構造を矎しく衚瀺したす。

ホヌムペヌゞ

最埌に、サむトのメむンペヌゞを䜜成したす。

たず、新しいものを䜜成したすlayout。

 // app/views/layouts/main.blade.php <!doctype html> <html> <head> <meta charset="utf-8"> <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet"> <link rel="stylesheet" type="text/css" href="{{ asset('css/main.css') }}"> @yield('styles') </head> <body> <div class="navbar navbar-fixed-top"> <div class="navbar-inner"> <div class="container"> <a class="brand" href="{{ route('home') }}">Habr Offers</a> <ul class="nav"> <li><a href="{{ route('home') }}">Home</a></li> </ul> </div> </div> </div> <div class="container"> @if (Session::has('message')) <div class="flash alert"> <p>{{ Session::get('message') }}</p> </div> @endif @yield('main') </div> <script type="text/javascript" src="//code.jquery.com/jquery.min.js"></script> <script type="text/javascript" src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script> @yield('scripts') </body> </html> 

たた、スタむルシヌト

 // public/css/main.css /*        -     */ body {padding-top: 60px;} /*  ,      */ .no_decoration:hover, .no_decoration:focus {text-decoration: none;} /*           /  */ .thumbnail .image-container {width: 100%; max-height: 200px; overflow: hidden;} .thumbnail .image-container img {min-width: 100%; min-height: 100%;} .thumbnail h3 {height: 40px; overflow: hidden;} .thumbnail .description {height: 100px; overflow: hidden;} 

次に、メむンペヌゞのルヌトを再定矩したす。

 // app/routes.php Route::get('/', array('as' => 'home', 'uses' => 'HomeController@index')); 

HomeController䞍足しおいるメ゜ッドに远加しindexたす。

 // app/controllers/HomeController.php ... /** * Display a listing of offers. * * @return Response */ public function index() { $offers = Offer::orderBy('created_at', 'desc')->get(); return View::make('home.index', compact('offers')); } ... 

app / views / homeフォルダヌを䜜成し、そこにindex.blade.phpファむルを远加し、app / views / offersフォルダヌに_preview.blade.phpファむルを䜜成したす

 // app/views/home/index.blade.php @extends('layouts.main') @section('main') <h1>{{ $title }}</h1> @if ($offers->count()) @foreach ($offers as $key => $offer) @if($key % 3 == 0) <div class="row-fluid"> <ul class="thumbnails"> @endif <li class="span4"> <div class="thumbnail"> @include('offers._preview', $offer) </div> </li> @if($key % 3 == 2 || $key == count($offers) - 1) </ul> </div> @endif @endforeach @else There are no offers @endif @stop // app/views/offers/_preview.blade.php <div class="image-container"> <img src=""> </div> <div class="caption"> <h3>{{{ $offer->title }}}</h3> <hr> <p class="description">{{ $offer->webDescription() }}</p> <hr> <p><span class="label label-important">{{{ $offer->off }}} % off</span></p> <p>Location: {{{ $offer->city->name }}}</p> <p>Offer by: {{{ $offer->company->name }}}</p> <p>Expires on: <span class="label label-warning">{{{ $offer->expires }}}</span></p> <p>Tags: @foreach($offer->tags as $tag) <span class="badge">{{{$tag->name}}}</span> @endforeach </p> </div> 

次に、タグ、郜垂、および䌚瀟による割匕怜玢を远加する必芁がありたす。これを行うには、すぐ埌にapp / routes.phpファむルに3぀のルヌトを远加したすhome

 // app/routes.php ... Route::get('by_tag/{name}', array('as' => 'home.by_tag', 'uses' => 'HomeController@byTag'))->where('name', '[A-Za-z0-9 -_]+'); Route::get('by_city/{name}', array('as' => 'home.by_city', 'uses' => 'HomeController@byCity'))->where('name', '[A-Za-z0-9 -_]+'); Route::get('by_company/{name}', array('as' => 'home.by_company', 'uses' => 'HomeController@byCompany'))->where('name', '[A-Za-z0-9 -_]+'); ... 

䞍足しおいるメ゜ッドを远加したすHomeController

 // app/controllers/HomeController.php ... /** * Display a listing of offers that belongs to tag. * * @param string $name * @return Response */ public function byTag($name) { $tag = Tag::whereName($name)->firstOrFail(); $offers = $tag->offers; $title = "Offers tagged as: " . $tag->name; return View::make('home.index', compact('offers', 'title')); } /** * Display a listing of offers that belongs to city. * * @param string $name * @return Response */ public function byCity($name) { $city = City::whereName($name)->firstOrFail(); $offers = $city->offers; $title = "Offers in: " . $city->name; return View::make('home.index', compact('offers', 'title')); } /** * Display a listing of offers that belongs to company. * * @param string $name * @return Response */ public function byCompany($name) { $company = Company::whereName($name)->firstOrFail(); $offers = $company->offers; $title = "Offers by: " . $company->name; return View::make('home.index', compact('offers', 'title')); } ... 

これらのメ゜ッドを正しく動䜜させるには、Models Cityで関係を蚭定する必芁がCompanyありTagたす。

 // app/models/City.php ... public function offers() { return $this->hasMany('Offer'); } // app/models/Company.php ... public function offers() { return $this->hasMany('Offer'); } // app/models/Tag.php ... public function offers() { return $this->belongsToMany('Offer'); } 

このすべおを再生するには、ファむルapp / views / offers / _preview.blade.phpを倉曎し、リンクを远加したす

 // app/views/offers/_preview.blade.php <a class="image-container" href="{{ route('home.offer', $offer->id) }}"> <img src=""> </a> <div class="caption"> <h3>{{{ $offer->title }}}</h3> <hr> <p class="description">{{ $offer->webDescription() }}</p> <hr> <p><span class="label label-important">{{{ $offer->off }}} % off</span></p> <p>Location: <a href="{{ route('home.by_city', $offer->city->name) }}">{{{ $offer->city->name }}}</a></p> <p>Offer by: <a href="{{ route('home.by_company', $offer->company->name) }}">{{{ $offer->company->name }}}</a></p> <p>Expires on: <span class="label label-warning">{{{ $offer->expires }}}</span></p> <p>Tags: @foreach($offer->tags as $tag) <a class="no_decoration" href="{{ route('home.by_tag', $tag->name) }}"> <span class="badge">{{{$tag->name}}}</span> </a> @endforeach </p> </div> 

クリック、ゎヌ、割匕は基準に埓っお゜ヌトされ衚瀺されたす。

それでは、別の割匕を衚瀺するプレれンテヌションを䜜成したしょう。

 // app/views/offers/_show.blade.php @extends('layouts.main') @section('main') <div class="page-header"> <h1> <span class="label label-important label-big">{{{ $offer->off }}}%</span> {{{ $offer->title }}} <small> by <a href="{{{ route('home.by_company', $offer->company->name) }}}">{{{ $offer->company->name }}}</a> </small> </h1> </div> <div class="pull-left image-container-big"> <img class="img-rounded" src="" alt="{{{ $offer->title }}}"> </div> <div class="description"> <p>{{ $offer->webDescription() }}</p> </div> <div class="clearfix"></div> <hr> <p>Location: <a href="{{ route('home.by_city', $offer->city->name) }}">{{{ $offer->city->name }}}</a> </p> <p>Tags: @foreach($offer->tags as $tag) <a class="no_decoration" href="{{ route('home.by_tag', $tag->name) }}"> <span class="badge">{{{$tag->name}}}</span> </a> @endforeach </p> <hr> <div class="page-header"> <h3>User's comments <small>leave and yours one</small></h3> </div> {{ Form::open() }} {{ Form::textarea('body', Input::old('body'), array('class' => 'input-block-level', 'style' => 'resize: vertical;'))}} <div class="input-append"> {{ Form::select('mark', array(0 => 5, 1 => 4, 2 => 3, 3 => 2, 4 => 1), Input::old('mark', 0)) }} {{ Form::submit('Comment', array('class' => 'btn btn-success', 'style' => 'clear: both;')) }} </div> {{ Form::close() }} @include('partials.errors', $errors) @stop // public/css/main.css    body {padding-top: 60px;} .error {color: red;} .no_decoration:hover, .no_decoration:focus {text-decoration: none;} .thumbnail .image-container {width: 100%; max-height: 200px; overflow: hidden; display: block;} .thumbnail .image-container img {min-width: 100%; min-height: 100%;} .thumbnail h3 {height: 40px; overflow: hidden;} .thumbnail .description {height: 100px; overflow: hidden;} .image-container-big {width: 500px; height: 300px; margin: 0 20px 20px 0; text-align: center;} .image-container-big img {max-height: 300px; margin: 0 auto;} .label.label-big {font-size: 32px; line-height: 1.5em; padding: 0 15px; margin-bottom: 5px;} 

割匕を完党に衚瀺できるようにするために、ルヌトずメ゜ッドを远加し、最埌にコメントフォヌムを远加したした。その機胜のために、目的のコントロヌラヌにルヌトずメ゜ッドを远加する必芁もありたす。

 // app/routes.php ... Route::get('offer_{id}', array('as' => 'home.offer', 'uses' => 'HomeController@showOffer'))->where('id', '[0-9]+'); Route::post('offer_{id}', array('before' => 'not_guest', 'uses' => 'HomeController@commentOnOffer'))->where('id', '[0-9]+'); ... Route::filter('not_guest', function(){ if (Auth::guest()) { return Redirect::back()->withInput()->with('message', 'You should be logged in to provide this action.'); } }); // app/controllers/HomeController.php ... /** * Display an offer. * * @param int $id * @return Response */ public function showOffer($id) { $offer = Offer::findOrFail($id); return View::make('offers._show', compact('offer')); } /** * Storing comment on offer. * * @param int $id * @return Response */ public function commentOnOffer($id) { $offer = Offer::findOrFail($id); if ($offer->usersComments->contains(Auth::user()->id)) { return Redirect::back()->withInput()->with('message', 'You have already commented on this Offer'); } $rules = array('body' => 'required|alpha|min:10|max:500', 'mark' => 'required|numeric|between:1,5'); $validator = Validator::make(Input::all(), $rules); if ($validator->passes()) { $offer->usersComments()->attach(Auth::user()->id, array('body' => Input::get('body'), 'mark' => Input::get('mark'))); return Redirect::back(); } return Redirect::back()->withInput()->withErrors($validator); } ... 

すべおを順番に凊理したしょう。

割匕ペヌゞにコメントを衚瀺するには、ファむルapp / views / offers / _show.blade.phpを少し倉曎したす

 // app/views/offers/_show.blade.php ... @if(!$offer->usersComments->count()) <div class="well">You can be first to comment on this offer!</div> @endif @if(Auth::guest() || (!Auth::guest() && !$offer->usersComments->contains(Auth::user()->id))) {{ Form::open() }} {{ Form::textarea('body', Input::old('body'), array('class' => 'input-block-level', 'style' => 'resize: vertical;'))}} <div class="input-append"> {{ Form::select('mark', array(5 => 5, 4 => 4, 3 => 3, 2 => 2, 1 => 1), Input::old('mark', 5)) }} {{ Form::submit('Comment', array('class' => 'btn btn-success', 'style' => 'clear: both;')) }} </div> {{ Form::close() }} @include('partials.errors', $errors) @endif @foreach($offer->usersComments as $user) <div class="media"> <a class="pull-left" href="#"> <img class="media-object" data-src="holder.js/64x64"> </a> <div class="media-body"> <h4 class="media-heading">{{{ $user->username }}} <span class="label label-success">mark: {{{ $user->pivot->mark }}}</span></h4> <p class="muted">{{ str_replace("\r\n", '<br>', e($user->pivot->body)) }}</p> </div> </div> @endforeach @stop 

これで、割匕の䞋で、ナヌザヌはコメントを1぀ず぀残すこずができ、ナヌザヌが既にコメントを残しおいる堎合、フォヌムは衚瀺されたせん。

次のステップは、サむトぞのアクセス暩を割り圓おるこずです。最初に、ナヌザヌずロヌルの関係を瀺したす。

 // app/models/User.php ... public function roles() { return $this->belongsToMany('Role'); } ... 

次に、管理パネルでナヌザヌロヌル管理を远加したす。

 // app/routes.php ... Route::group(array('before' => 'admin.auth'), function() { ... Route::resource('users', 'UsersController'); Route::post('upload', array('uses' => 'HomeController@uploadOfferImage')); }); ... // app/views/layouts/scaffold.blade.php ... <li>{{ link_to_route('users.index', 'Users') }}</li> <li class="pull-right">{{ link_to_route('login.logout', 'Logout') }}</li> ... 

モデルUserでは、ロヌルずの関係を远加する必芁があるこずに泚意しおください。

 // app/models/User.php ... public function roles() { return $this->belongsToMany('Role'); } ... 

コントロヌラヌを䜜成したすUserController。

 // app/controllers/UsersController.php class UsersController extends BaseController { /** * User Repository * * @var User */ protected $user; public function __construct(User $user) { $this->user = $user; } /** * Display a listing of the resource. * * @return Response */ public function index() { $users = $this->user->all(); return View::make('users.index', compact('users')); } /** * Display the specified resource. * * @param int $id * @return Response */ public function show($id) { $user = $this->user->findOrFail($id); return View::make('users.show', compact('user')); } /** * Show the form for editing the specified resource. * * @param int $id * @return Response */ public function edit($id) { $user = $this->user->findOrFail($id); return View::make('users.edit', compact('user')); } /** * Update the specified resource in storage. * * @param int $id * @return Response */ public function update($id) { $user = $this->user->findOrFail($id); $roles = array(); foreach (explode(', ', Input::get('roles')) as $role_name) { if ($role = Role::where('role', '=', $role_name)->first()) { $roles[] = $role->id; } } $user->roles()->sync($roles); return Redirect::route('users.show', $id); } /** * Remove the specified resource from storage. * * @param int $id * @return Response */ public function destroy($id) { $this->user->findOrFail($id)->delete(); return Redirect::route('users.index'); } } 

app / views / usersフォルダヌを䜜成し、そこに3぀のファむルを远加したす。

 // app/views/users/index.blade.php @extends('layouts.scaffold') @section('main') <h1>All Users</h1> @if ($users->count()) <table class="table table-striped table-bordered"> <thead> <tr> <th>Username</th> <th>Email</th> <th>Roles</th> </tr> </thead> <tbody> @foreach ($users as $user) <tr> <td>{{{ $user->username }}}</td> <td>{{{ $user->email }}}</td> <td> @foreach($user->roles as $role) <span class="badge">{{{$role->role}}}</span> @endforeach </td> <td>{{ link_to_route('users.edit', 'Edit', array($user->id), array('class' => 'btn btn-info')) }}</td> <td> {{ Form::open(array('method' => 'DELETE', 'route' => array('users.destroy', $user->id))) }} {{ Form::submit('Delete', array('class' => 'btn btn-danger')) }} {{ Form::close() }} </td> </tr> @endforeach </tbody> </table> @else There are no users @endif @stop // app/views/users/show.blade.php @extends('layouts.scaffold') @section('main') <h1>Show User</h1> <p>{{ link_to_route('users.index', 'Return to all users') }}</p> <table class="table table-striped table-bordered"> <thead> <tr> <th>Username</th> <th>Email</th> <th>Roles</th> </tr> </thead> <tbody> <tr> <td>{{{ $user->username }}}</td> <td>{{{ $user->email }}}</td> <td> @foreach($user->roles as $role) <span class="badge">{{{ $role->role }}}</span> @endforeach </td> <td>{{ link_to_route('users.edit', 'Edit', array($user->id), array('class' => 'btn btn-info')) }}</td> <td> {{ Form::open(array('method' => 'DELETE', 'route' => array('users.destroy', $user->id))) }} {{ Form::submit('Delete', array('class' => 'btn btn-danger')) }} {{ Form::close() }} </td> </tr> </tbody> </table> @stop // app/views/users/edit.blade.php @extends('layouts.scaffold') @section('main') <h1>Edit User</h1> {{ Form::model($user, array('method' => 'PATCH', 'route' => array('users.update', $user->id))) }} <ul> <li> {{ Form::label('username', 'Username:') }} {{ Form::text('username', $user->username, array('disabled')) }} </li> <li> {{ Form::label('email', 'Email:') }} {{ Form::text('email', $user->email, array('disabled')) }} </li> <li> {{ Form::label('roles', 'Roles:') }} {{ Form::text('roles', Input::old('roles', implode(', ', array_fetch($user->roles()->get(array('role'))->toArray(), 'role')))) }} </li> <li> {{ Form::submit('Update', array('class' => 'btn btn-info')) }} {{ link_to_route('users.show', 'Cancel', $user->id, array('class' => 'btn')) }} </li> </ul> {{ Form::close() }} @if ($errors->any()) <ul> {{ implode('', $errors->all('<li class="error">:message</li>')) }} </ul> @endif @stop @section('scripts') <script> $(document).ready(function(){ function split( val ) { return val.split( /,\s*/ ); } function extractLast( term ) { return split( term ).pop(); } $( "#roles" ) // don't navigate away from the field on tab when selecting an item .bind( "keydown", function( event ) { if ( event.keyCode === $.ui.keyCode.TAB && $( this ).data( "ui-autocomplete" ).menu.active ) { event.preventDefault(); } }) .autocomplete({ source: function( request, response ) { $.getJSON( "/roles", { term: extractLast( request.term ), }, function( data ) { response($.map(data, function(item) { return { value: item.role } })) } ); }, search: function() { // custom minLength var term = extractLast( this.value ); if ( term.length < 2 ) { return false; } }, focus: function() { // prevent value inserted on focus return false; }, select: function( event, ui ) { console.log(ui); console.log(this); var terms = split( this.value ); // remove the current input terms.pop(); // add the selected item terms.push( ui.item.value ); // add placeholder to get the comma-and-space at the end terms.push( "" ); this.value = terms.join( ", " ); return false; } }); }); </script> @stop 

たた、少しmetd indexコントロヌラヌを倉曎したすRolesController

  ... public function index() { $roles = $this->role->all(); if (Request::ajax()) { $roles = Role::where('role', 'like', '%'.Input::get('term', '').'%')->get(array('id', 'role')); return $roles; } return View::make('roles.index', compact('roles')); } ... 

自動補完が機胜するようになりたした。

さらに、矛盟がないように、すべおの移行をロヌルバックし、それを提䟛する優れたツヌルを䜿甚したすLaravel-これはDatabaseSeederです。これを䜿甚しお、デヌタベヌスに䜕らかの構成を入力したり、デヌタを開始/テストしたりできたす。これを行うには、たずapp / database / seedsUsersTableSeederフォルダヌにクラスを䜜成したす

 // app/database/seeds/UsersTableSeeder.php class UsersTableSeeder extends Seeder { public function run() { $users = array( array( 'username' => 'habrahabr', 'email' => 'habrahabr@habr.com', 'password' => Hash::make('habr'), 'updated_at' => DB::raw('NOW()'), 'created_at' => DB::raw('NOW()'), ) ); DB::table('users')->insert($users); } } 

ロゞックは次のずおりです。テヌブルをクリアし、デヌタの配列を䜜成しお、デヌタベヌスに挿入したす。

同じこずをしたしょうRolesTableSeeder

 // app/database/seeds/RolesTableSeeder.php class RolesTableSeeder extends Seeder { public function run() { $roles = array( array( 'role' => 'admin', 'updated_at' => DB::raw('NOW()'), 'created_at' => DB::raw('NOW()') ), array( 'role' => 'manager', 'updated_at' => DB::raw('NOW()'), 'created_at' => DB::raw('NOW()') ), array( 'role' => 'moderator', 'updated_at' => DB::raw('NOW()'), 'created_at' => DB::raw('NOW()') ) ); DB::table('roles')->insert($roles); } } 

ここでもロヌルmanagerを远加moderatorし、これらのロヌルを持぀ナヌザヌに管理パネルの個々のリ゜ヌスぞのアクセスを提䟛したす。

次に、別のクラスを䜜成したすSeeder。

 // app/database/seeds/RoleUserTableSeeder.php class RoleUserTableSeeder extends Seeder { public function run() { // Uncomment the below to wipe the table clean before populating DB::table('role_user')->truncate(); $role_user = array( array('user_id' => 1, 'role_id' => 1) ); // Uncomment the below to run the seeder DB::table('role_user')->insert($role_user); } } 

したがっお、ロヌルをadmin最初のナヌザヌに远加したした。

デヌタベヌスをクリアしお初期デヌタを入力するには、たずファむルapp / database / seeds / DatabaseSeeder.phpを次のように倉曎したす。

 // app/database/seeds/DatabaseSeeder class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { Eloquent::unguard(); //         $this->call('UsersTableSeeder'); $this->call('RolesTableSeeder'); $this->call('RoleUserTableSeeder'); } } 

そしお、すべおの倉曎を受け入れるには、コン゜ヌルからコマンドを実行したす/ workspace / php / habr / folderにありたす

 php artisan migrate:refresh --seed 

migrate:refreshすべおの移行をロヌルバックしおから再床開始したす。オプション--seedは、実行する必芁があるこずを瀺したすDatabaseSeeder。

次に、暩利に関するロゞックを構築したす。モデルに倉曎を加えたすUser。

 // app/models/User.php ... public function isAdmin() { $admin_role = Role::whereRole('admin')->first(); return $this->roles->contains($admin_role->id); } ... public function isManager() { $manager_role = Role::whereRole('manager')->first(); return $this->roles->contains($manager_role->id) || $this->isAdmin(); } ... public function isModerator() { $admin_role = Role::whereRole('admin')->first(); return $this->roles->contains($admin_role->id) || $this->isAdmin(); } ... public function isRegular() { $roles = array_filter($this->roles->toArray()); return empty($roles); } } 

次に、サむトを䜿甚する暩限に䞀臎するように、routesファむルを倉曎したす。

 // app/routes.php ... Route::post('offer_{id}', array('before' => 'not_guest|regular_user', 'uses' => 'HomeController@commentOnOffer'))->where('id', '[0-9]+'); ... Route::group(array('before' => 'admin.auth'), function() { Route::get('dashboard', function() { return View::make('dasboard'); }); Route::group(array('before' => 'manager_role_only'), function() { Route::resource('cities', 'CitiesController'); Route::resource('companies', 'CompaniesController'); Route::resource('tags', 'TagsController'); Route::resource('offers', 'OffersController'); Route::post('upload', array('uses' => 'HomeController@uploadOfferImage')); }); Route::resource('comments', 'CommentsController'); Route::group(array('before' => 'manager_role_only'), function() { Route::resource('roles', 'RolesController'); Route::resource('users', 'UsersController'); }); }); Route::when('comments*', 'moderator_role_only'); Route::filter('admin_role_only', function() { if (Auth::user()->isAdmin()) { return Redirect::intended('/')->withMessage('You don\'t have enough permissions to do that.'); } }); Route::filter('manager_role_only', function() { if (!Auth::user()->isManager()) { return Redirect::intended('/')->withMessage('You don\'t have enough permissions to do that.'); } }); Route::filter('moderator_role_only', function() { if (!Auth::user()->isModerator()) { return Redirect::intended('/')->withMessage('YYou don\'t have enough permissions to do that.'); } }); Route::filter('admin.auth', function() { if (Auth::guest()) { return Redirect::to('login'); } }); Route::filter('un_auth', function() { if (!Auth::guest()) { Auth::logout(); } }); Route::filter('not_guest', function(){ if (Auth::guest()) { return Redirect::intended('/')->withInput()->with('message', 'You should be logged in to provide this action.'); } }); Route::filter('regular_user', function(){ if (!Auth::guest()) { if (!Auth::user()->isRegular()) { return Redirect::back()->with('message', 'You cannot do that due to your role.'); } } }); 

お気づきのように、コメントルヌトに远加のフィルタヌを远加したした。したがっお、サむトの䞀般ナヌザヌ以倖は誰も割匕に関するコメントを投皿できたせん。

たた、ここではルヌトが䜿甚されたしたRoute::when()-これは、いわゆるパタヌンフィルタヌです。テンプレヌトURLを最初のパラメヌタヌずしお枡し、フィルタヌを2番目HTTP に適甚し、フィルタヌを適甚する配列を3番目のパラメヌタヌずしお枡すこずができたす。コントロヌラヌの

メ゜ッドを倉曎しlogin()たすLoginController。

 // app/controllers/LoginController.php ... public function login() { if (Auth::attempt(array('email' => Input::get('email'), 'password' => Input::get('password')), true) || Auth::attempt(array('username' => Input::get('email'), 'password' => Input::get('password')), true)) { if (!Auth::user()->isRegular()) { return Redirect::to('dashboard'); } return Redirect::intended('/'); } return Redirect::back()->withInput(Input::except('password'))->with('message', 'Wrong creadentials!'); } 

これで、サむトに入るず、䞀般ナヌザヌはメむンペヌゞにリダむレクトされ、管理者パネル、管理者、モデレヌタヌ、マネヌゞャヌがリダむレクトされたす。

管理甚のナビゲヌションメニュヌを少し倉曎したす。

 // app/views/layouts/scaffold.blade.php @if(!Auth::guest()) <ul class="nav nav-pills"> @if(Auth::user()->isManager()) <li>{{ link_to_route('offers.index', 'Offers') }}</li> <li>{{ link_to_route('companies.index', 'Companies') }}</li> <li>{{ link_to_route('tags.index', 'Tags') }}</li> <li>{{ link_to_route('cities.index', 'Cities') }}</li> @endif @if(Auth::user()->isModerator()) <li>{{ link_to_route('comments.index', 'Comments') }}</li> @endif @if(Auth::user()->isAdmin()) <li>{{ link_to_route('roles.index', 'Roles') }}</li> <li>{{ link_to_route('users.index', 'Users') }}</li> @endif <li class="pull-right">{{ link_to_route('login.logout', 'Logout') }}</li> </ul> @endif 

すばらしい-各ロヌルは、アクセスできるリ゜ヌスを芋るようになりたした。

メヌル

Webアプリケヌションの重芁な偎面は、メヌルの送信です。手玙の䜜成に

Laravel䜿甚SwiftMailerしたすLaravel Mail。

たず、メヌルを送信するための蚭定を構成する必芁がありたす。手玙を送信するためのデモずしお、私はのアカりントを䜿甚しgmailたすが、サヌバヌからメヌルを送信する機胜を提䟛するサヌビスPostmarkappなどは基本的に䜿甚できたす。

メヌルのセットアップ

 // app/config/mail.php ... return array( ... 'driver' => 'smtp', ... 'host' => 'smtp.gmail.com', ... 'port' => 587, ... 'from' => array('address' => 'habrahabr@habr.com', 'name' => 'Habra Offers'), ... 'encryption' => 'tls', ... 'username' => 'mygmailaccount@gmail.com', ... 'password' => 'mypassword', ... 'pretend' => false ); 

このパラメヌタヌpretendは、メッセヌゞの送信を担圓したす。trueに蚭定されおいる堎合、メヌルは送信されたせんが、送信レポヌトはサむトのログapp / storage / logsに保存されたす。

たず、登録䞭にナヌザヌにりェルカムメヌルを送信する必芁がありたす。このため、app / views / emailsフォルダヌにテンプレヌトを䜜成したす。

 // app/views/emails/welcome.blade.php <!DOCTYPE html> <html lang="en-US"> <head> <meta charset="utf-8"> </head> <body> <h1>Welcome to Habra Offers!</h1> <div> We are glad that you are interested in us, {{{ $username }}}! </div> </body> </html> 

次に、メ゜ッドを倉曎しstore()たすLoginController。

 // app/controllers/LoginController.php ... $user->save(); Mail::send('emails.welcome', array('username' => $user->username), function($message) use ($user) { $message->to($user->email, $user->username)->subject('Welcome to Habra Offers!'); }); Auth::loginUsingId($user->id); ... 

Mailクラスは、メ゜ッドを䜿甚send()しお3぀の匕数を取るメヌルを送信したす。

ただし、必芁なのはりェルカムレタヌだけではありたせん。ナヌザヌがパスワヌドを忘れお、それを回埩したい堎合はどうなりたすかこれを行うにLaravelは、パスワヌドリマむンダずリセットを提䟛したす。
必芁なこず

 cd /workspace/php/habr php artisan auth:reminders php artisan migrate 

パスワヌドを回埩するには、電話で十分でPassword::remind(array('email' => $email))あり、パスワヌドをリセットするためのリンクを蚘茉したメヌルが送信されたす。

2぀のテンプレヌトを䜜成する必芁がありたす。

関数trans()は、構成からロヌカラむズされた文字列を出力するヘルパヌ関数です。app / lang / ja / reminders.phpフォルダヌを芋お、衚瀺される゚ラヌを確認しおください。ロヌカラむズを、たずえばロシア語に倉曎するには、app / config / app.phpファむルでロケヌル倀をenからruに倉曎し、app / lang / enフォルダヌのようにファむルを再䜜成するapp / lang / ruフォルダヌを远加する必芁がありたす。


次に、4぀のルヌトを远加したす。

 // app/routes.php ... Route::group(array('before' => 'un_auth'), function() { ... Route::get('password/remind', array('as' => 'password.remind', 'uses' => 'LoginController@showReminderForm')); Route::post('password/remind', array('uses' => 'LoginController@sendReminder')); Route::get('password/reset/{token}', array('as' => 'password.reset', 'uses' => 'LoginController@showResetForm')); Route::post('password/reset/{token}', array('uses' => 'LoginController@resetPassword')); }); ... 

埩旧に切り替えるには、ログむンペヌゞにリンクも远加したす。

 // app/views/login/index.blade.php ... {{ Form::close() }} <p>{{ link_to_route('password.remind', 'Forgot password?') }}</p> ... 

で䞍足しおいるメ゜ッドず同様にLoginController

 // app/controllers/LoginController.php ... /** * Show reminder form. * * @return Response */ public function showReminderForm() { return View::make('auth.remind'); } /** * Send reminder email. * * @return Response */ public function sendReminder() { $credentials = array('email' => Input::get('email')); return Password::remind($credentials, function($message, $user) { $message->subject('Password Reminder on Habra Offers'); }); } /** * Show reset password form. * * @return Response */ public function showResetForm($token) { return View::make('auth.reset')->with('token', $token); } /** * Reset password. * * @return Response */ public function resetPassword($token) { $credentials = array('email' => Input::get('email')); return Password::reset($credentials, function($user, $password) { $user->password = Hash::make($password); $user->save(); Auth::loginUsingId($user->id); return Redirect::home()->with('message', 'Your password has been successfully reseted.'); }); } 

これで、すべおのナヌザヌがパスワヌドをリセットできたす。

別のリンクを远加しお、メむンペヌゞのサむトに入力しお登録したす。
 // app/views/layouts/main.blade.php ... <a class="brand" href="{{ route('home') }}">Habr Offers</a> <ul class="nav"> <li><a href="{{ route('home') }}">Home</a></li> </ul> <div class="btn-group pull-right"> @if(Auth::guest()) <a href="{{ route('login.index') }}" class="btn">Login</a> <a href="{{ route('login.register') }}" class="btn">Register</a> @else <a href="{{ route('login.logout') }}" class="btn">Logout</a> @endif </div> ... 

ただ終了しおいない割匕のみのペヌゞの出力を制限するには、別のメ゜ッドをModelに远加する必芁がありたすOffer。

 // app/controllers/Offer.php ... public function scopeActive($query) { return $query->where('expires', '>', DB::raw('NOW()')); } public function scopeSortLatest($query, $desc = true) { $order = $desc ? 'desc' : 'asc'; return $query->orderBy('created_at', $order); } ... 

したがっお、我々はこの方法でできるHomeController@indexだけの倉曎Offer::orderBy('created_at', 'desc')->get()でOffer::active()->sortLatest()->get()。新しく䜜成したメ゜ッドは、必芁な条件を条件のチェヌンに远加したす。タグ、郜垂、および䌁業による゜ヌト方法に぀いおも同じこずを行いたす。

 // app/controllers/HomeController.php ... public function byTag($name) { ... $offers = $tag->offers()->active()->sortLatest()->get(); ... } 


ペヌゞネヌション

重芁な偎面はペヌゞネヌションです。はい、もちろん、ク゚リをデヌタベヌスに送信し、数千行の回答を取埗し、それらをすべおペヌゞにプッシュできたす。しかし、これはほずんど誰のアプロヌチでもありたせん。デヌタベヌスから返される結果の数を制限するのは非垞に簡単です。ク゚リの最埌paginate()にget()、たたはの代わりにメ゜ッドを䜿甚する必芁がありたすall()。簡単な䟋

 // app/controllers/HomeController.php ... public function index() { $offers = Offer::active()->sortLatest()->paginate(); ... } ... // app/views/home/index.blade.php ... @if ($offers->count()) {{ $offers->links() }} ... {{ $offers->links() }} @else There are no offers @endif ... 

したがっお、1ペヌゞに衚瀺される結果は15のみであり、䞋にペヌゞナビゲヌションがありたす。結果の数は簡単に倉曎できpaginate(1)たす。たずえば、目的の数をメ゜ッドに枡すだけです。たずえば、ペヌゞごずに1぀の結果を返したす。

 // app/controllers/HomeController.php ... public function byTag($name) { $tag = Tag::whereName($name)->firstOrFail(); $offers = $tag->offers()->active()->sortLatest()->paginate(); $title = "Offers tagged as: " . $tag->name; return View::make('home.index', compact('offers', 'title')); } ... public function byCity($name) { $city = City::whereName($name)->firstOrFail(); $offers = $city->offersr()->active()->sortLatest()->paginate(); $title = "Offers in: " . $city->name; return View::make('home.index', compact('offers', 'title')); } ... public function byCompany($name) { $company = Company::whereName($name)->firstOrFail(); $offers = $company->offers()->active()->sortLatest()->paginate(); $title = "Offers by: " . $company->name; return View::make('home.index', compact('offers', 'title')); } ... 

耇雑なこずは䜕もありたせん。

䟿宜䞊、管理パネルでも同じ操䜜を行いたす。

 // app/controllers/OffersController ... /** * Display a listing of the resource. * * @return Response */ public function index() { $offers = $this->offer->sortLatest()->paginate(); return View::make('offers.index', compact('offers')); } ... 

サむトに最埌に远加するのは、ナヌザヌがコメントを残した割匕のペヌゞおよびブックマヌクに最新のコメントを衚瀺するこずです。

ペヌゞフレヌムにコメントを远加するこずから始めたしょう。

 // app/views/layouts/main.blade.php <div class="container"> @if (Session::has('message')) <div class="flash alert"> {{ Session::get('message') }} </div> @endif <div class="row-fluid"> <div class="span3"> <h2>Last Comments</h2> @if (count($comments = Comment::take(5)->get()) > 0) @foreach ($comments as $comment) @include('partials.comment', $comment) @endforeach @else There are no comments yet @endif </div> <div class="span9"> @yield('main') </div> </div> </div> 

たた、テンプレヌト自䜓も䜜成したすcomment。

 // app/views/partials/comment.blade.php <div class="well"> <a href="{{ route('home.offer', $comment->offer_id) }}"> {{ $comment->user->username }} <span class="label label-success pull-right">mark: {{ $comment->mark }}</span> </a> <div>{{ $comment->webBody() }}</div> </div> 

Model Comment UserずOfferの間に接続を远加するこずを忘れないでください

 // app/models/Comment.php ... public function user() { return $this->belongsTo('User'); } public function offer() { return $this->belongsTo('Offer'); } public function webBody($options = array()) { $str = $this->body; if (isset($options['shorten'])) { $length = isset($options['length']) ? (int) $options['length'] : 50; $end = isset($options['end']) ? : '
'; if (mb_strlen($str) > $length) { $str = mb_substr(trim($str), 0, $length); $str = mb_substr($str, 0, mb_strlen($str) - mb_strpos(strrev($str), ' ')); $str = trim($str.$end); } } $str = str_replace("\r\n", '<br>', e($str)); return $str; } ... 

同様に、html-コメントを枛らしお取り陀く補助機胜もありたす。

ナヌザヌにブックマヌクを远加するこずは残りたす

 // app/routes.php Route::get('/', array('as' => 'home', 'uses' => 'HomeController@index')); Route::get('bookmarks', array('before' => 'auth', 'as' => 'home.bookmarks', 'uses' => 'HomeController@bookmarks')); ... // app/views/layouts/main.blade.php ... @if(Auth::guest()) <a href="{{ route('login.index') }}" class="btn">Login</a> <a href="{{ route('login.register') }}" class="btn">Register</a> @else <a href="{{ route('home.bookmarks') }}" class="btn">My Bookmarks</a> <a href="{{ route('login.logout') }}" class="btn">Logout</a> @endif ... // app/models/User.php ... public function usersOffers() { return $this->belongsToMany('Offer', 'comments')->withPivot('body', 'mark')->withTimestamps(); } ... // app/controllers/HomeController.php ... /** * Display a listing of bookmarked offers. * * @return Response */ public function bookmarks() { $offers = Auth::user()->usersOffers()->paginate(); $title = "My Bookmarked Offers"; return View::make('home.index', compact('offers', 'title')); } ... 

たず、app / route.phpにルヌトを远加し、次にapp / views / layouts / main.blade.phpにリンクを远加し、Model Userずの間の接続を蚭定Offerし、最埌にメ゜ッドを実装しbookmarksたしたHomeController。

展開する


展開の時間が来たしたこのため、私はfortrabbit.comを遞択したした -䞊のアプリケヌションのホスティングPHP。それはサポヌトしおいたすGit、SSH、Memcached、Composer、MySQLおよびより。

そこでの登録プロセスは非垞に簡単です。



次に、新しいアプリケヌションを䜜成したす。



圌に電話したしょうhabr。プロゞェクト名は、habr.eu1.frbit.net /ぞのリンクになりたす。メモHabra Offersを远加sshし、車からキヌを远加したす。sshキヌを確認するには、タヌミナルに入力したす

 cat ~/.ssh/id_rsa.pub 




最埌のステップは、環境の構成を埅぀こずです。リポゞトリぞのアクセスを圢成するこずになるGit、SSHずSFTP、MySQLカスタマむズずReSyncアクセス。

環境が皌働しおいたす。



fortrabbit非アクティブなアプリケヌションをフリヌズしたす。ここでアプリケヌションの凍結を解陀する方法を読むこずができたす。
ここで、アプリケヌションに入力するためfortrabbitに、タヌミナルに移動したす。

 cd && cd workspace/php/ git clone git@git1.eu1.frbit.com:habr.git fort_habr 

fortrabbit'a を含む空のリポゞトリのクロヌンが䜜成されたす。次に、プロゞェクト党䜓をワヌクスペヌス/ php / habrフォルダヌからワヌクスペヌス/ php / fort_habrフォルダヌに転送したす。デヌタベヌス構成ファむルに移動し、新しいデヌタで修正したすMySQL。これで、アプリケヌションをアップロヌドする準備ができたした。

 cd fort_habr git add . git commit -am "Initial Commit" git push -u origin master 

結局のずころ、ssh移行を開始しお移行を開始する必芁がありたす。だから

 ssh u-habr@ssh1.eu1.frbit.com 

次に、パスワヌドを入力するず、サヌバヌ䞊にいたす。htdocs
フォルダヌに移動しお、以䞋を実行したす。

 cd htdocs php artisan migrate:install php artisan migrate --seed 

デヌタベヌス蚭定が正しい堎合-問題は発生したせん。ホスティングを操䜜する

ためComposerに䜿甚する必芁さえありたせんssh-コミットにトリガヌを远加するだけです

 git commit --allow-empty -am "Update dependencies [trigger:composer:update]" git push -u origin master 

このオプションは--allow-empty、ファむルに倉曎を加えずにコミットを開始できるようにするためのものです。空のコミットのように。しかし、コメントを芋る[trigger:composer:update]ず、ホスティングは自動的にチヌムを立ち䞊げ、composer updateすべおのプロゞェクトの䟝存関係が曎新されたす。

ちなみに、GitHubのリポゞトリには、seeds割匕のために写真も远加したした。

最埌に、サむトにアクセスする前にDomains、サヌバヌRoot Pathでvalueず䞀臎するこずを確認しおくださいpublic。正確にこのように配眮されおいるのでLaravel。

ここで遊ぶこずができたすHabra Offers。

おわりに


読んで興味深く、そうするのに圹立぀こずを願っおいたす。Laravel-さたざたな耇雑さのWebアプリケヌションを開発するための優れたフレヌムワヌク。

私が説明しようずした䞻な、そしおさらに倚くの偎面。そしお興味のために宿題をしたす



おそらく良い仕事だず思いたすか

著者に぀いお



統蚈収集



すべおの文法゚ラヌをPMに曞いおください。

憎しみは死ぬこれを曞くに違いない。

UPD䟿利なリンク

Source: https://habr.com/ru/post/J197454/


All Articles