ララヴェル アプリケーションのインストール、構成、作成、デプロイ

だから、あなたはLaravelフレームワークを試してみるか、学びたいと思っています。



他のPHP



フレームワークに精通している場合-難しいことではありませんが、そうでない場合-これは最初のフレームワークの優れた選択肢です。



Laravel-職人のためのPHPフレームワーク!



記事は非常に大きいです。 週末に完全に読むことをお勧めします。



怠け者の場合:

Github

アプリ







設置



Laravelをインストールするには、 Composerが必要です

Composerは、 PHP



依存関係管理ツールです。 プロジェクトに必要な依存ライブラリを宣言し、プロジェクトにインストールできます。

- 作曲家



環境は*nix



環境にインストールされます(サイトにはWindowsにインストールするためのマニュアルがあり、さらにWAMPGitなどのサーバーが必要です)。



非常にクリーンな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:便利なリンク




All Articles