LaravelでJsonでレスポンスする場合のリレーションを含める方法は、Model(Eloquent

LaravelでJsonでレスポンスする場合のリレーションを含める方法は、Model(Eloquent)のtoArray()をオーバーライド

前提知識

  • コントローラーで単純にreturnした場合には、toJson()が自動で呼ばれる。
  • toJson()の内部でtoArray()が呼ばれる。
  • toArray()をオーバーライドしてそこで、リレーション先やアクセサなどのプロパティーを追加できる。

著者クラスがあったとして、booksのhasManyのリレーションと、年齢を返すageアクセサをtoJson()に含める例。

class Authoer extends Model{

    public function books(){
        return $this->hasMany(....)
    }

    public function getAgeAttribute(){
        return ....
    }

.............中略..................


    public function toArray()
    {
        return array_merge(parent::toArray(), [
            'books'=>$this->books,
            'age'=>$this->age
        ]);
    }   
    
}   

PhotoShopのテキストボックス内で色が違う場合のSVG書き出し方法

PhotoShopのテキストボックス内で色が違う場合のSVG書き出し方法。

タイトルの状態のテキストボックスをシェイプに変換すると色が一色になってしまう現象があったので、対処法。(なにかいい方法ないかなぁ。)

  1. テキストレイヤーを「選択」(複数レイヤーの場合はまとめて選択)
  2. 右クリックで「シェイプに変換」(同じ色になる一部の色が消える※1)
  3. 書き出し形式からSVGへ書き出し( or 右クリックでSVGコピーして手動で保存)
  4. イラレで開く
  5. レイヤーをコピー
  6. 上のレイヤーを非表示
  7. 下のレイヤーの色が消えてしまった色にする
  8. 下のレイヤーのいらないパスを削除(消えた色以外の場所)
  9. 下のレイヤーを非表示/上のレイヤーを表示
  10. 上のレイヤーのいらないパスを削除
  11. 下のレイヤーを表示

※1 複数の文字色を使っているテキストレイヤーをシェイプに変換すると、範囲の多い文字色にすべて変換されてしまうようでした。

Docker環境のLaravel5.5で、実行環境でDBのホスト名を切り替えたい場合

Docker環境のLaravel5.5で、実行環境でDBのホスト名を切り替えたい場合
php_sapi_name()関数で環境を判定して /config/database.phpで切り替えて上げればOK
‘cli’ならポートフォワーディングしているのホスト(親機)の127.0.0.1で接続
(apacheモジュールの場合は’apache2handler’)

        'mysql' => [
            'driver' => 'mysql',
            'host' => (php_sapi_name()=='cli')? '127.0.0.1':env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],

Laravel よく使うものまとめメモ

Laravel よく使うものまとめ (ver5.5.x)

Eloquent

User::all(); //全て
User::where('role' , 'admin')->get(); //whereなど条件後のすべて
User::where('role' , 'admin')->first();
User::query()->chunk(100, function($users){
    foreach($users as $user){
        //
    }
});
foreach (User::where('role', 'member')->cursor() as $user) {
    //PDOStatement::fetchを利用して1行ごとに取得
}

//find系
$user = User::find(10);//id指定
$users = User::find([10,324]);//idリストで指定
$user = User::findOrFail(1); // 無い時 'ModelNotFoundException' 例外をthrow
$user = User::where('email', 'test@example.com')->findOrFail(); 
※ModelNotFoundException はスルーすると 404 を返す。
$users = User::withTrashed()->get();    //削除済も含める
$users = User::onlyTrashed()->get();    //削除済のみ
$user = User::withTrached()->find(1)->restore();    //restore()で削除を解除
User::find(1)->forceDelete(); //完全削除

//date関係
$user->updated_at = now(); // now()は Carbonインスタンスを返す
$user = User::find(1);
echo $user->updated_at->getTimestamp();


//集計
User::all()->count();
User::all()->max('age');

//追加
$user = User::create(['email' => 'test@example.com']);
$user = User::firstOrCreate(['email' => 'test@example.com']);   //なければcreate
$user = User::updateOrCreate(['email' => 'test@example.com']);  // update もしくは create
$user = new User();
$user->email = 'test@example.com';
$user->save();


//Update
$user = User::find(1);
$user->email = 'test02@example.com';
$user->save();

//Multiple Update (savedとupdated イベントが発行されない)
$users = User::where('type','admin')->update(['is_admin' => 1]);

//delete
$user = User::find(1);
$user->delete();
User::destroy(1);
User::destroy([1,20,345]);
User::where('updated_at' , '<' , '2018-01-01 00:00:00')->delete();


//Model 

namespace App;

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

class User extends Model
{

    use SoftDeletes;

    protected $table = 'members';   //命名規則外の場合のテーブル名
    protected $primaryKey = 'user_id'; //命名規則外の主キー
    protected $guarded = ['id','password','email']; // or fillableで許可
    protected $hidden = ['password','remember_token']; // 隠すもの
    protected $dates = [
        'created_at',
        'updated_at',
        'deleted_at'
    ];
    
   public static function boot()
    {
        parent::boot();
        self::updating(function ($model) {
            $text = implode('_', [$model->name, $model->kana]);
            $text = mb_convert_kana($text, 'KVas', 'UTF-8');
            $text = preg_replace("/(\s| )/", "", $text);
            $model->search_text = $text;
        });
    }
    
    public function getFullNameAttribute(){
        return $this->lastname . ' ' . $this->firstname;
    }
    public function setKanaAttribute($value){
        $this->attributes['kana'] = mb_convert_kana($value, 'KV', 'UTF-8');
    }
    public function kaimyou(){
        return $this->hasOne('App\Kaimyou');
    }   
    public function role(){
        return $this->belongsTo('App\Role');
    }
    public function items(){
        return $this->hasMany('App\Item');
    }
    public function types(){
        return $this->belongsToMany('App\Type');
    }
}


//relation 
$user = User::find(1);
$user->kaimyou(); // 1:1
$user->role(); // n:1
$user->items(); // 1:n
$user->types(); // n:n
$user->types()->withPivot('note'); //チェックボックスのその他の入力欄など
//relationのsave/create
$user->items->save($item);
$user->items->saveMany([ $item1 , $item2 ]);
$user->items->create(['name'=>'薬草']);
$user->items->createMany(['name'=>'薬草'],['name'=>'毒消し草']);
//親realationへの所属
$admin_role = Role::getAdmin();
$user->role()->associate($admin_role);
$user->save();
//n:nの関連付け / 切り離し
$user->types()->attach($type_id);
$user->types()->attach($type_id,['note' => '特殊波動攻撃']);
$user->types()->attach([
    2,
    32 => ['note' => '召喚呪術']
]);     //複数をattache 1つ目は通常、2番目はnote付き
$user->types()->detach($type_id); //または [$type_id,$type_id]
$user->types()->detach();   //すべて切離
$user->sync([2 , 4 , 7]); //指定したものだけが残る。
$user->toggle([2 , 4 , 7]); //指定したものをトグル

//親の updated_atを自動的に変更
class Item extends Model
{
    protected $touches = ['user'];
    
    public function user(){
        return $this->belongsTo('App\User');
    }
}


//Eager ロード/withでget()するとquery回数が減らせる。
$users = User::with('role')->get();
foreach ($users as $user) {
    echo $user->role->name;
}
//Eager load 複数
$users = User::with(['role','items'])->get();
foreach ($users as $user) {
    echo $user->role->name;
    var_dump($user->items);
}
//Eager loadのネスト
$users = User::with('items.supplier')->get();
foreach ($users as $user) {
    foreach($user->items as $item){
        echo $item->name;
        echo $item->supplier->name;
    }
}
//Eager load 条件付
if(まとめてロードしたい){
    $user->laod('items');
}




//Scopeスコープ
class User extends Model
{

    public function scopePublish($query){
        return  $query->where('publish',1);     
    }
    
    public function scopeStatus($query , $status){
        return $query->where('status',$status);
    }
    .
    .   
}

User::publish()->get(); //引数なし
User::status('draft')->get();   //引数有り

コレクション

$users = User::get();   //コレクションで取得
$users->all();  //すべて返す

計算

$users->avg('age');
$users->median('age');//中央値
$users->mode('age');//最頻値
$users->max('age');
$users->min('age');
$users->sum('point'); //合計

加工

$users->pluck('age'); // [40,34,28,22]
$users->pluck('age','id'); // ['1'=>40,'2'=>34,'3'=>28,'4'=>22]
$user = $users->random(); //ランダム
$users = $users->shuffle(); //シャッフルします。
$userPages = $user->chunk(10);//10ごとに分割
$allUsers = $users->concat($users2); 
$users = $users->merge($users2);
$users = $users->reverse(); //反転(keyはそのまま)
$users = $users->sortBy('age');//年齢でソート コールバックでソートも可能/逆はsortByDesc
$users = $users->sortByDesc(function($user,$key){
    return $user['point'] + $user['gold'];  //ポイントとゴールドを合計した値で逆ソート
});
$users->mapWithKeys(function($user,$key) {  //加工してkey,valueの形に
        return [$user['id'] => $user['name']];
})->toArray();

変換出力

$users->toArray();
$users->toJson();
$users->transform(function($user,$key){
    return 加工したオブジェクト;
});
$users->values();   //keyを0から打ち直し

追加/削除/抜き出しなど

$user = $users->first();//最初
$user = $users->last();//最後
$user = $users->pop();//最後を削除して取り出し
$users->push($user);//最後に追加
$user = $users->shift();    //最初取り出し削除
$user = $users->firstWhere('role','admin');//最初の管理者
$user = $users->firstWhere('age' , '>=' , 40);//最初の40歳以上
$users = $users->slice(5,3); //5番目から3つ抜き出し
$users = $users->splice(5,3); //5番目から3つ抜き出して"削除" 
$userGroups = $users->split(3); // 3つに分割

判定

$users->isEmpty();  //また、$users->isNotEmpty()も有り
$users->contains('email', 'test@example.com'); //true or false またはcontainsStrict も有
$users->when(条件,function($users){   //条件がtrueの場合に実行 ->例えば、ある条件で攻撃力がみんな1.5倍
    $users->transform(function($user,$key){
        return $user->attack *= 1.5;
    });
});

検索/フィルタ/処理系/その他


$users = $users->where('age',40); //40歳のみ
$users = $users->whereIn('age',[10,20,30]); // 10,20,30
$users = $users->whereNotIn('age',[18,28]); // 18,28以外
$users = $users->filter(function($user , $key){
    return $user->age > 40; //40歳以上でフィルタ
});
$users = $users->reject(function($user , $key){
    return $user->sex == 1;     //男性を省く
});
$users->map(function($user,$key){   //加工して新しいコレクション
    $user->point = random(1,100);
    return $user;
});
$users->each(function($user,$key){
    if(条件){
        return false; // ループ終了
    }
});
//macro 機能追加
Collection::macro('toUpper', function () {
    return $this->map(function ($value) {
        return Str::upper($value);
    });
});

Higher Order Message

//経験値 500以上のユーザーモデルにlevelupメソッドを実行
$users = User::where('exp', '>', 500)->get();
$users->each->levelup();
(こう書ける?? 例外どうするか要検討)
User::where('exp' , '>' , 500)->get()->each->levelup();

//全員のポイントの総合計
$users = User::get();
return $users->sum->point;
(こう書ける?)
$users = User::get()->sum->point;

ヘルパー

配列

$arr = array_add(['name'=>'desk'] , 'price', 100); //['name'=>'desk', 'price' =>100]
$arr = array_collapse([1,2,3],[4,5,6]); // [1,2,3,4,5,6]
list($keys,$values) = array_divide(['name' => 'des,']); // $keys ['name'] , $values ['desk']
$arr = array_except(['name'=>'desk','price'=>100],['price']); //['name'=>'desk']
$result = array_random([1,2,3,4,5,6]); // どれか

パス

app_path('Http/Controller');    //  /path/to/project/app/Http/Controller
base_path('vendor/bin');    //      /path/to/project/vendor/bin
config_path('app.php')/     //      /path/to/project/config/app.php
$path = mix('css/app.css');     //バージョン付したMixファイルのパス
public_path();      //publicのパス
storage_path();         //  strageディレクトリのパス

文字列

camel_case('foo_bar');  // fooBar
echo e('foo<br>bar');   // foo&lt;br&gt;
kebab_case('fooBar');   //foo-bar
snake_case('fooBar');       //foo_bar

その他

$app = app();       //サービスコンテナを返す。
$user = auth()->user(); //authenticatorインスタンスを返す。
bcrypt('ヒミツのパスワード');  //ハッシュ表ジュウン
$value = config('app.timezone', $default); //コンフィグ値取得 /config/app.phpの'timezone'を取得
$env = env('APP_ENV');  // 取得
event(new UserRegistered($user));   //イベント発行

画面遷移(URL,リダイレクト系)

//redirect
abort(403); //例外
abort(403, 'Unauthorized.', $headers);      //例外+メッセージヘッダー
return redirect($to = null, $status = 302, $headers = [], $secure = null);
return redirect('/home');
return redirect()->route('route.name');
return back($status = 302, $headers = [], $fallback = false);   //引数はデフォルト return back();で戻る。
//URL
action('UserController@index'); //  例えば https://example.com/user
asset('img/photo.jpg');     //アセットへのhttp(s)://へのフルパス
route('user.edit',20);          //例えば https://example.com/user/20 
route('user.edit',['id'=>20,'type'=>'client']);     //複数パラメータ Controllerのアクションで変数名($id,$type)で受取可
url('user/index');  //http://localhost/user/index

セッション

$value = session('key');
session(['chairs' => 7, 'instruments' => 3]);

tap()

$array = tap(Client::findOrFail(1), function ($client) use $name {
    $client->title = $name;
})->toArray();

ログ

//infoレベル
info('Some helpful information!');
info('User login attempt failed.', ['id' => $user->id]);
//debugレベル
logger('Debug message');
//errorレベル
logger()->error('You are not allowed here.');
$now = now(); // Illuminate\Support\Carbonインスタンス
$value = old('value');  // 直前のフォーム

参考

Macで外付けディスクを2つつないでコピーはrsyncで簡単だった

Macで外付けディスクを2つつないでそのままコピーしたら普通に激重たかったのでメモ。

rsyncでよいのか簡単だった。

$ rsync -avE /Volumes/旧HDD名/ /Volumes/新HDD名/

mp4動画をチャプターごとに分割して音声ファイルにするメモ

お題 mp4動画をチャプターごとに分割して音声ファイルにする。

ffmpegとmkvtookとmp4v2をインストール

$ brew install ffmpeg
$ brew install mkvtoolnix
$ brew install mp4v2 

下ごしらえ、動画をチャプターに分割が重いので、事前に音声のみにする。(-vnはビデオなし、音声もこの時点でエンコード5.1ch > ステレオとかしても良いかも)

$ ffmpeg -i org.mp4 -vn -acodec copy tmp.mp4

mkvtoolnixについてくるmkvmergeコマンドで分割

$ mkvmerge -o output.mkv --split chapters:all tmp.mp4 

今回音声コーデックがaacだったのですが、拡張子aacだとiTuneに取り込めず、m4aに
音声が5.1chとかだとm4aへ変換でエラーになるので、その場合は -ac 2 とかつけてステレオにしてあげるビットレートも-ab 480kとか指定してあげる

(今回はaacだったのでそのまま -acodec copyで流し込むだけ)

$ ffmpeg -i output-001.mkv -vn -acodec copy output-001.m4a

mp4v2のツールでタグ編集

$ mp4tags -A "アルバム名" -a "アーティスト名" -albumartist "アルバムアーティスト名" -t トラックNo 対象ファイル

dockerのmailcatcherでLaravelのメールをテストするenvの設定

dockerのmailcatcherでLaravelのメールをテストするenvの設定

MAIL_DRIVER=smtp
MAIL_HOST=mailcatcher
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

docker-composeは以下の感じ

version: '3'
services:
  web:
    build: ./docker/web
    privileged: true
    ports:
      - 80:80
    volumes:
      - ./src:/var/www/html
      - ./docker/web/httpd-laravel.conf:/etc/httpd/conf.d/httpd-laravel.conf
      - ./docker/web/php-laravel.ini:/etc/php.d/php-laravel.ini
    depends_on:
      - mysql
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: laravel
      MYSQL_USER: laravel
      MYSQL_PASSWORD: pass
    ports:
      - 3306:3306
    volumes:
      - ./db/init/:/docker-entrypoint-initdb.d
      - ./db/data/:/var/lib/mysql
  mailcatcher:
    image: schickling/mailcatcher
    ports:
      - 1080:1080
      - 1025:1025

laravel-mixでコンパイルした場合に日本語フォント名がエスケープされて、Webフォントが使えなくなった

laravel-mixでコンパイルした場合に日本語フォント名がエスケープされて、Webフォントが使えなくなることがありましたが、laravel-mixのイベントフック(Event Hooks)を使ってコンパイル後にエスケープ前の変換して解決しました。(もうちょっとスマートにできないかなぁ。。悩み)

Webフォントのサービスは。REALTYPE(https://www.realtype.jp)
以下のように日本語だとOK

.hoge {
    font-family : 'A+mfCv-AXIS Font ベーシック UL ProN';
}

laravel-mixでsassコンパイルすると以下のようにエスケープされてNG

.hoge {
    font-family : 'A+mfCv-AXIS Font \30D9\30FC\30B7\30C3\30AF   UL ProN';
}

次のようにして解決

then()でコンパイル後のイベントをフックして読み込んで置換(split-join)して再度書き込んでます。


//元のフォント名  'A+mfCv-AXIS Font ベーシック UL ProN'
//エスケープされたフォント名  'A+mfCv-AXIS Font \\30D9\\30FC\\30B7\\30C3\\30AF   UL ProN'

mix
    .sass('style.sass', 'dist/css/')
    .then(() => {
        let css = fs.readFileSync('dist/css/style.css', {encoding: "utf-8"});
        css = css.split('\\30D9\\30FC\\30B7\\30C3\\30AF  ').join('ベーシック');
        fs.writeFileSync('dist/css/style.css', css);
    });

参考 : laravel-mix : EventHooiks
https://github.com/JeffreyWay/laravel-mix/blob/master/docs/event-hooks.md

dockerのWordPressのファイルアップロード上限を上げる。

dockerのWordPress使っていて、画像アップロードで上限にひっかかりエラーになる。
dockerのWordPressコンテナのphp.iniで設定してあげればいい。
毎度設定大変なので、docker-compose.ymlにリンクの設定を書く。

(WordPressコンテナのvolumesにphp.iniを追加)

    volumes:
      - ./dist:/var/www/html
      - ./php.ini:/usr/local/etc/php/php.ini

全体はこんな感じ

version: "3.5"
#services:
#  nginx:
#    image: nginx:latest
#    ports:
#      - "80:80"
#    volumes:
#      - ./dist:/usr/share/nginx/html
services:
  wordpress:
    image: wordpress:latest
    ports:
      - 80:80
    links:
      - wordpress-db
    environment:
      WORDPRESS_DB_HOST: wordpress-db:3306
      WORDPRESS_DB_NAME: my_db
      WORDPRESS_DB_USER: my_db_user
      WORDPRESS_DB_PASSWORD: my_db_user_pass
    volumes:
      - ./dist:/var/www/html
      - ./php.ini:/usr/local/etc/php/php.ini
  wordpress-db:
    image: mysql:5.7
    ports:
      - 3306:3306
    environment:
      MYSQL_ROOT_PASSWORD: my_db_user_pass_root
      MYSQL_DATABASE: my_db
      MYSQL_USER: my_db_user
      MYSQL_PASSWORD: my_db_user_pass
    volumes:
      - ./db/mysql_init:/docker-entrypoint-initdb.d
      - ./db/mysql_data:/var/lib/mysql

元々 WordPressコンテナには php.ini は無く、php.ini-development,php.ini-productionのみがあるので、リンクしても大丈夫そう。

$ docker exec xxxx_wordpress_1 ls /usr/local/etc/php
conf.d
php.ini
php.ini-development
php.ini-production

pythonでのスクレイピングメモ

pythonでのスクレイピングメモ

スクレイピングのメモ。

//Toolをpipでインストール
$ pip install reqests
$ pip install lxml
$ pip install cssselect
$ pip install feedparser
#サンプル
import requests
import lxml.html

#html取得
r = requests.get('{URL}')
html = r.text

#オブジェクト化
root = lxml.html.fromstring(html)

#xpath
elements = root.xpath("/html/{XPATH}")

#selecter
elements = root.cssselect("body h1")

#for
for tag in elements:
    print(tag.text) 
import feedparser
rss = feedparser.parse('https://www.shoeisha.co.jp/rss/index.xml')
print(rss)
print(rss.version)
print(rss['feed']['title'])
for content in rss['entries']:
    print(content['title'])
    

Page 5 of 46

Powered by WordPress & Theme by Anders Norén