- 投稿日:2019-11-15T20:57:59+09:00
Laravel でSeeder実行時に指定できるオプション
LaravelでSeeder実行するときにオプションをつけれるのですが、他にどのようなのがあるのか
見てみたので纏めてみました。
既出だと思いますが、温かい目でよろしくおねがいします。指定のSeederを実行する
db:seed --class=SampleSeederデータベースを指定する
db:seed --database=testingなおここの
--database='のあとに指定するものは、
app/config/database.phpに記載している
connections`を指定します。運用中に実行する
db:seed --force実行していないのですが、
env
がproduction
になっているものに対しても、実行するときに使用するものです。
コードを見ると警告がでるようです。db:seedのコード
以下興味ある人用
vendor/laravel/framework/src/Illuminate/Database/Console/Seeds/SeedCommand.php<?php namespace Illuminate\Database\Console\Seeds; use Illuminate\Console\Command; use Illuminate\Database\Eloquent\Model; use Illuminate\Console\ConfirmableTrait; use Symfony\Component\Console\Input\InputOption; use Illuminate\Database\ConnectionResolverInterface as Resolver; class SeedCommand extends Command { use ConfirmableTrait; /** * The console command name. * * @var string */ protected $name = 'db:seed'; /** * The console command description. * * @var string */ protected $description = 'Seed the database with records'; /** * The connection resolver instance. * * @var \Illuminate\Database\ConnectionResolverInterface */ protected $resolver; /** * Create a new database seed command instance. * * @param \Illuminate\Database\ConnectionResolverInterface $resolver * @return void */ public function __construct(Resolver $resolver) { parent::__construct(); $this->resolver = $resolver; } /** * Execute the console command. * * @return void */ public function handle() { if (! $this->confirmToProceed()) { return; } $this->resolver->setDefaultConnection($this->getDatabase()); Model::unguarded(function () { $this->getSeeder()->__invoke(); }); $this->info('Database seeding completed successfully.'); } /** * Get a seeder instance from the container. * * @return \Illuminate\Database\Seeder */ protected function getSeeder() { $class = $this->laravel->make($this->input->getOption('class')); return $class->setContainer($this->laravel)->setCommand($this); } /** * Get the name of the database connection to use. * * @return string */ protected function getDatabase() { $database = $this->input->getOption('database'); return $database ?: $this->laravel['config']['database.default']; } /** * Get the console command options. * * @return array */ protected function getOptions() { return [ ['class', null, InputOption::VALUE_OPTIONAL, 'The class name of the root seeder', 'DatabaseSeeder'], ['database', null, InputOption::VALUE_OPTIONAL, 'The database connection to seed'], ['force', null, InputOption::VALUE_NONE, 'Force the operation to run when in production'], ]; } }
- 投稿日:2019-11-15T20:04:15+09:00
【Laravel5.8】複数のデータベースに接続するときは全てのモデルに`$connection`を書こう
先日のEagerロード先テーブルは必ず主キーもSELECTしないといけないって記事を見るとわかりますが、Eagerロードでは複数テーブルを結合するのにJOINを使わず、データを別々に取ってきて後から合体させています。
つまり、これは異なるデータベースのテーブルに疑似リレーションを張れるのでは?
テーブルAclass TableA extends Model{ // テーブル名 protected $table = 'table_a'; /** * リレーション * @return BelongsTo */ public function tableb() { return $this->belongsTo(TableB::class, 'b_id', 'id'); } }別DBのテーブルBclass TableB extends Model{ // データベースがちがう protected $connection = 'other_database'; // テーブル名 protected $table = 'table_b'; /** * リレーション * @return BelongsTo */ public function tablea() { return $this->belongsTo(TableA::class, 'a_id', 'id'); } }コントローラから呼び出す。
コントローラ$columnsAB = TableA->with('table_b')->get(); $columnsBA = TableB->with('table_a')->get();さて、この動作はどうなるでしょうか?
答えは、
$columnsAB
は想定通りに取得できる、$columnsBA
は取得できない、です。
わかりづら!どうしてこんなことになるのでしょうか。
TableA→TableBの順で呼んだ場合、まずデフォルトのDBからTableAを呼び、次いでTableBを見に行った時点でDBが'other_database'に切り替わるのでTableBのデータも正常に取得できる、ということになります。
逆にTableB→TableAの順で呼んだ場合、TableBを見に行った時点でDBが'other_database'に切り替わってTableBのデータを正常に取得し、次いでTableAを見に行きますがDBは'other_database'のままでありTableAが読めない、となります。
原因はTableAに
$connection
が書かれていないせいです。
普段$connection
を書いてないときは暗黙的にdefault
が選ばれるので、何も書かずとも問題なく動作します。
しかし一度でも別のデータベースが選ばれると、その後は$connection
を書かないかぎりそのDBが選ばれ続けてしまうみたいです。まとめ
複数のデータベースに接続する可能性があるときは、デフォルト接続先にも必ず
$connection
を書いておくと安全かもしれない。
- 投稿日:2019-11-15T17:28:49+09:00
laravel サーバ環境構築基礎
少なくとも一つのフレームワークを覚えようと思って、複数のフレームワークの中ララベルに興味ありました。
いろいろ調べたところ、テスト環境の情報が多い、サーバ環境構築の情報が少ない、バラバラです。
一応、苦戦二日間やっと構築成功しました、以下に纏めます。共有致します。この手順で恐らく一発でできそうな感じ。
OS CentOS 6.10 32bit
php5.6.40
laravel5.4
mysql5.6/40
Apache2.2.15■ composer インストール
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
composer■ laravel インストール
composer global require "laravel/installer"■ プロジェクトを作成
composer create-project --prefer-dist laravel/laravel laravelapp(laravelapp プロジェクト名です)
■ 権限変更
chmod 777 storage -R
chmod 777 bootstrap/cache -R■ httpd.conf 編集
AllowOverride None -> AllowOverride All
(ご自身の環境合わせ設定)■ config/app.php 編集
timezone = 'Asia/Tokyo',
locale = 'ja',
'fallback_locale' => 'en',■ laravelapp/.env 編集
DB_DATABASE=mydb
DB_USERNAME=root
DB_PASSWORD=test
(必要応じて)■ laravelapp/app/Providers/AppServiceProvider.php 編集
public function boot()
{
Schema::defaultStringLength(191); //追加■ httpd 再起動
リモートサーバのURLでlaravelの起動画面と任意の
web.phpの指定のパスで動作確認OK。(完了)
次は関連のAPP作成予定です。
- 投稿日:2019-11-15T17:07:13+09:00
学習のアウトプット #1
はじめに
なぜQiitaでの投稿を始めたかというと、現在PHP/Laravel絶賛勉強中なのですが教材を見てコードを書いてるばかりで全然アウトプットしておらず、学習のしたことがイマイチ身についてないなぁと感じたからです。
早速アウトプットしていこうと思うのですが、今回はOSについて書こうと思います。
ある時プログラミングを学習していて、OSって言葉が目に付いて「あれ、OSってなんだっけ??」と思ったためです。
OSとは
OSとはオペレーティングシステムの略で、PCを管理・操作するシステムです。
よく聞くのはwindows,macOS,linuxだったり、スマホのiOSやAndroidもOSです。OSがないとPCは動きません。
昔のゲームに例えるとゲーム機本体があってもソフトがないからゲームできないみたいな感じですかね。OSの機能
- 入出力の制御
キーボードをどのキーを何回押したか、マウスをどの方向にどれだけ動かしたか、などのキーボードやマウスの入出力の制御やモニターの画面表示の機能はOSが担っています。この機能によって画面にエディタが表示され、コードを書くことができ、カーソルを動かすことができるわけです。
- ファイル管理 フォルダやファイルの管理もOSの役割です。
この機能のおかげで、コードを書いたファイルをフォルダに入れたり出したり、保存や表示することができます。
タスク管理
ブラウザでググりながらエディタでコードを書いていく、というように複数のアプリを表示させたり、アプリの起動や終了等の管理・制御もOSの機能です。その他にも様々な機能
上記以外にもマウスやキーボード、外付けのハードディスクなどのハードウェアの管理などPCの基本的な部分はOSが担っています。さいごに
とまぁこんな感じで学習のアウトプットとして投稿を続けていけたらと思っています。
- 投稿日:2019-11-15T16:10:46+09:00
Laravel 6.x(またはLumen 6.x)でDynamoDBに接続する
SDKのインストール
AWS SDK for PHPをLaravelに取り込みましょう。
プロジェクトルートで下記コマンドを実行します。$ composer require aws/aws-sdk-php
これでvendor配下にSDKがインストールされます。
DynamoDBに接続する
AWS SDK for PHPでつなぐにはこのようにします。
下記の例ではクレデンシャルを含めていますが、この方法は推奨されていないので注意してください
ハードコードされた認証情報の使用use Aws\Sdk; $awsSdk = new Sdk([ 'credentials' => [ 'key' => アクセスキー, 'secret' => シークレットキー, ], 'endpoint' => 'エンドポイント', 'region' => 'リージョン', 'version' => 'バージョン' ]); $dynamo = $awsSdk->createDynamoDb();以上。
Laravelらしくしよう
と、まあこれじゃあちょっとLaravelらしくないよね、ってことでDIしてみます。new禁止。
envに登録
dotEnv便利ですよね。
.envAWS_DYNAMO_KEY=アクセスキー AWS_DYNAMO_SECRET=シークレットキー AWS_DYNAMO_ENDPOINT=エンドポイント AWS_DYNAMO_REGION=リージョン AWS_DYNAMO_VERION=バージョンServiceProviderを記述
LaravelでDIといえばServiceProviderですよね。
app/Providers/DynamoDbServiceProvider.php<?php declare(strict_types=1); namespace App\Providers; use Aws\DynamoDb\DynamoDbClient; use Aws\Sdk; use Illuminate\Support\ServiceProvider; class DynamoServiceProvider extends ServiceProvider { /** * Dynamo AWS-SDK接続 */ public function register() { $this->app->singleton(DynamoDbClient::class, function () { $awsSdk = new Sdk([ 'credentials' => [ 'key' => env('AWS_DYNAMO_KEY'), 'secret' => env('AWS_DYNAMO_SECRET'), ], 'endpoint' => env('AWS_DYNAMO_ENDPOINT'), 'region' => env('AWS_DYNAMO_REGION'), 'version' => env('AWS_DYNAMO_VERSION') ]); return $awsSdk->createDynamoDb(); }); } }Laravelなら config/app.phpのproviders に登録。
config/app.php'providers' => [ App\Providers\DynamoServiceProvider::class,Lumenならbootstrap/app.phpに登録します。
bootstrap/app.php$app->register(App\Providers\DynamoServiceProvider::class);コンストラクタインジェクション
ここまでできたら、お好きな場所でインジェクションできます。Facadeにしてもいいかもですね。
/** * @var DynamoDbClient */ private $client; /** * constructor. * @param DynamoDbClient $client */ public function __construct(DynamoDbClient $client) { $this->client = $client; }Eloquentっぽくしよう
Dynamoにアクセスするのに毎回
$this->client->putItem($paramertes);とかって書くのめんどいですよね。
普段使い慣れているEloquentやQueryBuilderっぽく、find()とかsave()とかdestory()とか使いたいです。じゃあ、EloqentModelに変わる基底クラスを作っちゃいましょう。
DynamoModelを作る
たとえばこんな感じでしょうか。
<?php declare(strict_types=1); namespace App\Models\Dynamo; use Aws\DynamoDb\DynamoDbClient; use Aws\DynamoDb\Exception\DynamoDbException; use Aws\DynamoDb\Marshaler; use Aws\Result; use Illuminate\Support\Str; /** * Class DynamoModel * @package App\Models\Dynamo */ class DynamoModel { /** * @var string */ protected $primaryKey = 'id'; /** * @var string|null */ protected $table = null; /** * @var DynamoDbClient */ private $client; /** * @var Marshaler */ private $marshaler; /** * DynamoModel constructor. * @param DynamoDbClient $client * @param Marshaler $marshaler */ public function __construct(DynamoDbClient $client, Marshaler $marshaler) { $this->client = $client; $this->marshaler = $marshaler; } /** * @return string */ public function getTable(): string { $tableName = $this->table ?? Str::snake(Str::pluralStudly(class_basename($this))); return env('APP_ENV', 'local') . '_' . $tableName; } /** * DynamoにINSERT * @param array $attributes * @return Result */ public function save(array $attributes): Result { $marshalItem = $this->marshaler->marshalItem($attributes); $dynamoParameters = [ 'TableName' => $this->getTable(), 'Item' => $marshalItem ]; try { return $this->client->putItem($dynamoParameters); } catch (DynamoDbException $exception) { throw $exception; } } /** * Dynamoからレコード削除 * @param array $ids */ public function destroy(array $ids): void { foreach ($ids as $id) { $deleteParameter = [$this->primaryKey => $id]; $marshalItem = $this->marshaler->marshalItem($deleteParameter); $dynamoParameters = [ 'TableName' => $this->getTable(), 'Key' => $marshalItem ]; try { $this->client->deleteItem($dynamoParameters); } catch (DynamoDbException $exception) { throw $exception; } } } /** * Dynamoから1件レコードを取得 * @param int $id * @return array|null */ public function find(int $id): ?array { $idParameter = [$this->primaryKey => $id]; $marshalItem = $this->marshaler->marshalItem($idParameter); $dynamoParameters = [ 'TableName' => $this->getTable(), 'Key' => $marshalItem ]; try { $awsResult = $this->client->getItem($dynamoParameters)->get('Item'); if (is_null($awsResult)) { return null; } return $this->marshaler->unmarshalItem($awsResult); } catch (DynamoDbException $exception) { throw $exception; } } }あとはこれを継承したModelをつかえば、EloquentみたいにDynamoにアクセスできるようになります。
必要に応じで、findOrFail()でもupdateOrCreate()でもはやしていけばいいのかなーと思います。
- 投稿日:2019-11-15T13:59:27+09:00
Laravelメールアドレス確認メールでhttpsだと403 | Invalid signatureで認証できない問題への対応
メールアドレス確認のURLをhttpsにする
商用環境はhttpsで運用されると思います。
デフォルトでは、httpでメールアドレス確認のURLが生成されてしまいますので、
以下の対応を行います。/app/Providers/AppServiceProvider.php<?php namespace App\Providers; use Illuminate\Support\Facades\Schema; use Illuminate\Support\ServiceProvider; use Illuminate\Routing\UrlGenerator; class AppServiceProvider extends ServiceProvider { public function boot(UrlGenerator $url) { # 開発環境(local)はhttpなので、httpsにしない if (config('app.env') !== 'local') { $url->forceScheme('https'); } } }
AppServiceProvider.php
で、bootにUrlGeneratorを渡して、
urlがhttpsで生成されるようにしています。これで、メールアドレス確認メールのhttps化は完了です。
確認してみると403 | Invalid signature
メールアドレス確認URLは、httpのままだと成功しましたが、httpsにすると失敗します。
メールアドレス確認URLで、403 | Invalid signatureを解決する
解決策として、TrustedProxyでプロキシを
'*'
に設定します。
IPがわかる時など、全てを許可したくないときは、以下ドキュメントを参考にプロキシを設定しましょう。
https://readouble.com/laravel/5.8/ja/requests.html#configuring-trusted-proxies/app/Http/Middleware/TrustProxies.php<?php namespace App\Http\Middleware; use Illuminate\Http\Request; use Fideloper\Proxy\TrustProxies as Middleware; class TrustProxies extends Middleware { /** * The trusted proxies for this application. * * @var array */ protected $proxies = '*'; //これを'*'にするだけ /** * The headers that should be used to detect proxies. * * @var int */ protected $headers = Request::HEADER_X_FORWARDED_ALL; }解決時の環境について
この問題を解決時の私のLaravelバージョンは
5.8.13
です。
デプロイ先はHerokuです。まとめ
- url生成をhttpsに変更する
- IPアドレスが分からないときは、全プロキシを信用させる
参考にしたページ
url生成をhttpsにする
https://qiita.com/hisash/items/4b3bb1ea47c38b0d8c86
今回の問題の解決
https://github.com/laravel/framework/issues/28311
同じと思われる問題に直面してる例
https://github.com/laravel/framework/issues/30594
Laravelドキュメントの信用するプロキシの設定
https://readouble.com/laravel/5.8/ja/requests.html#configuring-trusted-proxies
- 投稿日:2019-11-15T06:42:46+09:00
Laravelでartisanコマンドを自作してCommandを体感する
Laravelを勉強してて、php artisan という不思議なコマンドを何気なく使っていましたが
これを自作できるということで、自作した勉強メモです。artisanコマンド一覧を見てみる
まずはどんなコマンドがあるのか見てみることにしました。
php artisan listこのコマンドを使うことで、どんなコマンドが使えるのか、またそのコマンドの説明が表示されます。
Laravel Framework 6.0.4 Usage: command [options] [arguments] ... make make:channel Create a new channel class make:command Create a new Artisan command make:controller Create a new controller class make:event Create a new event class make:exception Create a new custom exception class make:factory Create a new model factory make:job Create a new job class make:listener Create a new event listener class make:mail Create a new email class make:middleware Create a new middleware class make:migration Create a new migration file make:model Create a new Eloquent model class ...ズラーと表示されました。中には馴染みのあるmake:migrationなんかもあります。
Create a new migration fileと書いてあるので、コマンドの簡単な説明も一緒に表示されていることがわかります。この中に
make:command Create a new Artisan commandというのがあるので、これを使ってコマンドを作ります。
php artisan make:command SampleCommand Console command created successfully.これで
app/Console/Commands/SampleCommand.php
というファイルが作成されました。
ここにどんなコマンドなのか具体的に記載していきます。コマンドの名前の指定や、説明、handleメソッドにコンストラクタがあります。
どんなコマンドにするか考える
せっかくなので何かできるコマンドにしたかったので
テーブルのレコードをアップデートするコマンドにしました。jsonファイルを読み込ませて
その内容に従ってテーブルのレコードをアップデートします。
アップデートが終わったらjsonファイルをDoneフォルダに移動させます。まぁ、実用性はないですが、、、。。。
{ "table": "customers", "where": "id", "conditions":"1", "update": "name", "value":"高田健志" }コマンドファイルを編集する
まずは名前を変更します。
SampleCommand.php//protected $signature = 'command:name'; protected $signature = 'myupdate:table {filename}';これがartisanコマンドになります。
使うときはphp artisan myupdate:table Sample.jsonというようになります。
{arg}で引数を指定することもできます。次にコマンドの説明を変更します。
SampleCommand.php//protected $description = 'Command description'; protected $description = 'update table';この状態でもう一回listを表示してみます。
myupdate myupdate:table update table登録したコマンド名と説明が表示されています。
あとはコマンドの処理を実装するだけです。コマンドの処理の中身はhandleメソッドに書いていきます。
SampleCommand.php/** * Execute the console command. * * @return mixed */ public function handle() { //引数のファイル名を取得する $filename = $this->argument('filename'); //infoは緑色の文字でメッセージを表示する $this->info("start update table command!"); //引数のファイルがなければ処理を終わりにする if( !file_exists(base_path('MyUpdate/'.$filename)) ){ dd("ファイルが無いお(;_;)"); } // jsonファイルを解析する $json_data = file_get_contents(base_path('MyUpdate/'.$filename)); $result = json_decode($json_data); $this->info("before..."); //jsonのデータでテーブルからレコードを取得する。 $target = \DB::table($result->table) ->where($result->where,$result->conditions) ->first(); //取得したレコードを表示する var_dump($target); // jsonのデータでテーブルのレコードを更新する $query = \DB::table($result->table) ->where($result->where,$result->conditions) ->update([ $result->update => $result->value ]); $this->info("after..."); $target = \DB::table($result->table) ->where($result->where,$result->conditions) ->first(); var_dump($target); // 終えたらファイルを移動する // もしDoneフォルダがなければ作成する if( !file_exists(base_path('MyUpdate/Done')) ){ mkdir(base_path('MyUpdate/Done')); } // jsonファイルを移動する rename(base_path('MyUpdate/'.$filename),base_path('MyUpdate/Done/'.$filename)); $this->info("end update table command!"); }jsonの中身でテーブルを指定して
レコードの取得と更新を行います。処理が終わったらjsonファイルを移動させます。
base_pathメソッドは、プロジェクトルートの完全パスを返してくれる、ヘルパ関数です。
https://readouble.com/laravel/5.5/ja/helpers.html#method-base-pathこのコマンドには穴がありますが、その辺は置いといて実行したいと思います。
MyUpdateフォルダをプロジェクトルートに作成して
先ほどの中身と同じSample.jsonを作成します。作成したコマンドを実行します。
php artisan myupdate:table Sample.json start update table command! before... object(stdClass)#681 (4) { ["id"]=> int(1) ["name"]=> string(9) "横山緑" ["created_at"]=> string(19) "2019-10-15 10:07:53" ["updated_at"]=> string(19) "2019-10-15 10:07:53" } after... object(stdClass)#684 (4) { ["id"]=> int(1) ["name"]=> string(12) "高田健志" ["created_at"]=> string(19) "2019-10-15 10:07:53" ["updated_at"]=> string(19) "2019-10-15 10:07:53" } end update table command!コマンドを打つことで
jsonファイルを読み取って、レコードを検索して更新して、フォルダの作成とファイルの移動をする。という
なんとなーくコマンドっぽいのができました。
- 投稿日:2019-11-15T01:26:06+09:00
dockerイメージphp:7.2-apacheでImageMagickをインストール
インストールのイメージ
ImagicをインストールするためのImageMagickのインストールが必要。
まずPHPへImageMagicのインストールを行い(※dockerfileへ記述)、その後、Imagicのライブラリをcomposerに追加する。インストール方法
php側でimagemagickとimagickのインストールが必要なため、dockerfileに下記を追加。
&& apt-get update && apt-get install -y imagemagick libmagickwand-dev \ && pecl install imagick \ && docker-php-ext-enable imagick \これでbuild後、docker-compose up -d !!!ッッッターーーーーン!!!!
Linux側で、下記コマンドを叩いてImagicのライブラリを追加。
$composer require spatie/pdf-to-image $composer require spatie/pdf-to-text※バージョン確認やパッケージの種類は下記パッケージリストを確認してください。
https://packagist.org/packages/spatie/pdf-to-imagehttps://packagist.org/packages/spatie/pdf-to-imageすると、composer.jsonのrequireに下記が追加されてる。
"spatie/pdf-to-image": "^1.8", "spatie/pdf-to-text": "^1.2"ImageMagickを使えるようになったか下記コマンドで確認
# convert -versionインストールできてたらこんなんがでるよ。
# convert -version Version: ImageMagick 6.9.7-4 Q16 x86_64 20170114 http://www.imagemagick.org Copyright: © 1999-2017 ImageMagick Studio LLC License: http://www.imagemagick.org/script/license.php Features: Cipher DPC Modules OpenMP Delegates (built-in): bzlib djvu fftw fontconfig freetype jbig jng jp2 jpeg lcms lqr ltdl lzma openexr pangocairo png tiff wmf x xml zlibちなみに、dockerfileに書いた
&& docker-php-ext-enable imagick \は、extensionを有効にするけん、 php.iniに extension=imagick.so を書く必要がなくなる!!!
書いてると重複するけん下記のエラーがでる。
PHP Warning: Module 'imagick' already loaded in Unknown on line 0以上です。