- 投稿日:2021-01-06T12:26:30+09:00
composer require実行時につまずいたエラー
laravelでAuthを導入しようと下記コマンドを実行
composer require laravel/ui:2.2すると下記エラーが出る
Fatal error: Allowed memory size of 1610612736 bytes exhausted (tried to allocate 4096 bytes) in phar:///usr/local/bin/composer/src/Composer/DependencyResolver/RuleWatchGraph.php on line 52 Check https://getcomposer.org/doc/articles/troubleshooting.md#memory-limit-errors for more info on how to handle out of memory errorsどうやらメモリーが足りず実行できないよう。
laravel/uiに限らずcomposer requireを実行しようとするとこうなる。解決方法
COMPOSER_MEMORY_LIMIT=-1 composer 〇〇これで一時的にメモリを確保できるようだ。
- 投稿日:2021-01-06T11:43:50+09:00
Cloud9でLaravelの環境構築の方法
手順
PHPをインストール
$ sudo yum -y update $ sudo yum -y install php72 php72-mbstring php72-pdo php72-intl php72-pdo_mysql php72-pdo_pgsql php72-xdebug php72-opcache php72-apcu $ sudo unlink /usr/bin/php $ sudo ln -s /etc/alternatives/php7 /usr/bin/php $ php -vcomposerをインストール
$ curl -sS https://getcomposer.org/installer | php $ sudo mv composer.phar /usr/bin/composerLaravelをインストール
$ composer global require "laravel/installer" $ composer create-project laravel/laravel アプリ名 6.* --prefer-dist
- 投稿日:2021-01-06T02:54:49+09:00
LaravelのユニットテストでDBにないエラーを明確にする
すみませんが、日本語訳はあとにします。
Problem
When
seeInDatabase
asserting was failed, I can’t not known where the path of properies is not match.Example: With bellow test, I will receive the bellow message.
Test code:
$this->seeInDatabase('les_knowledge', [ 'lesson_id' => $lesson->id, 'number' => 1, 'content_en' => '~ years old DIFFFFFFFFFF', 'explanation_en' => 'In Japan, you are born as a 0 year-old and turn 1 on your first birthday.', ]);Test result:
Unable to find row in database table [les_knowledge] that matched attributes
javascript
[{
"lesson_id":98,"number":1,
"content_en":"~ years old DIFFFFFFFFFF",
"explanation_en":"In Japan, you are born as a 0 year-old and turn 1 on your first birthday."
}]
It is hard to find where is the path don’t match.
Solution
Create a function likes a bellow.
function seeInDatabaseAndHasProperties($table, array $filter, array $properties, $connection = null){ $this->seeInDatabase($table, $filter, $connection); $model = (array)DB::table($table)->where($filter)->first(); $this->assertEquals($properties, Arr::only($model, array_keys($properties))); }Test code will be:
$this->seeInDatabaseAndHasProperties('les_knowledge', [ 'lesson_id' => $lesson->id, 'number' => 1, ], [ 'content_en' => '~ years old DIFFFFFFFFFF', 'explanation_en' => 'In Japan, you are born as a 0 year-old and turn 1 on your first birthday.', ]);Test result will be:
Failed asserting that two arrays are equal. --- Expected +++ Actual @@ @@ Array ( - 'content_en' => '~ years old DIFFFFFFFFFF' + 'content_en' => '~ years old' 'explanation_en' => 'In Japan, you are born as a 0...thday.' )Now you can easily see which properties don’t match.
Original post is: https://khoinv.com/post/639359603705020416/clear-errors-not-found-in-the-database-laravel
- 投稿日:2021-01-06T02:53:20+09:00
Laravelで明確配列タイプをする
すみませんが、日本語訳はあとにします。
The origin code
public function get_user_badge(array $user_ids, array $course_ids, Carbon $from, Carbon $end, $direction = 'desc'){}Refactoring 1: clarify array type
Util Classes
use Illuminate\Support\Collection; class IntegerCollection extends Collection { public function __construct(int ...$values) { parent::__construct($values); } } class UserIds extends IntegerCollection {} class CourseIds extends IntegerCollection {}The code will rewrite as bellow:
public function get_user_badge(UserIds $userIds, CourseIds $courseIds, Carbon $from, Carbon $end, $direction = 'desc'){} // Usage $userIds = new UserIds($user_ids); $courseIds = new CourseIds($course_ids); get_user_badge($userIds, $courseIds, ...)Refactoring 2: Combine $from/$end to InclusiveRange class
Util Classes
abstract class BaseRange { public $begin; public $end; public function __construct(Carbon $begin, Carbon $end) { $this->begin = $begin; $this->end = $end; } //...some ultil functions in here } class InclusiveRange extends BaseRange{}The code will rewrite as bellow:
public function get_user_badge(UserIds $userIds, CourseIds $courseIds, InclusiveRange $range, $direction = 'desc'){} // Usage $inclusiveRange = new InclusiveRange($from, $end) get_user_badge($userIds, $courseIds, $inclusiveRange...)Original post is: https://khoinv.com/post/639390454460432384/clarify-type-in-laravel-code
- 投稿日:2021-01-06T02:51:11+09:00
Re-wirte test-case with magic numbers 1, 2, 4
すみませんが、日本語訳はあとにします。
The original code
private client_fixture(){ return Client::firstOrFail(); } private message_fixture($client, $count = 1, $adjust_min_version = 0, $adjust_max_version = 0){ return factory(Notification::class, $count)->create([ 'app_version_min' => $client->version + $adjust_min_version, 'app_version_max' => $client->version + $adjust_max_version, ]); } public function it_returns_only_messages_for_correct_app_version(){ $client = $this->client_fixture(); $this->message_fixture($client) $this->assertEqual(1, Notification::forAppVersion($client->version)->count()); } public function it_returns_only_messages_for_valid_range_app_version(){ $client = $this->client_fixture(); $this->message_fixture($client, 2, -rand(0, 1), rand(0, 1) $this->assertEqual(2, Notification::forAppVersion($client->version)->count()); } public function it_does_not_return_messages_for_invalid_range_app_version(){ $client = $this->client_fixture(); $this->message_fixture($client, 4, -rand(2, 3), -rand(1, 2) $this->assertEqual(0, Notification::forAppVersion($client->version)->count()); }Refacoring code
public function it_returns_only_messages_for_correct_app_version(){ $client = $this->client_fixture(); $this->message_fixture($client, $correct_version_message_count = 1) $this->message_fixture($client, $valid_range_message_count = 2, -rand(0, 1), rand(0, 1) $this->message_fixture($client, $_invalid_range_message_count = 4, -rand(2, 3), -rand(1, 2) $this->assertEqual($correct_version_message_count + $valid_range_message_count, Notification::forAppVersion($client->version)->count()); }Note 1, 2, and 4 are the magic numbers of the above test because the expected result of the test is 3, but there is only one way to get 3 by combining (1 + 2).
- Pros: Only one test can cover three independent test cases, and a combination of three conditions can be tested in the same test case.
- Cons: If the code is wrong, it will take longer to debug.
Original post is: https://khoinv.com/post/639393766259736576/re-wirte-test-case-with-magic-numbers-1-2-4
- 投稿日:2021-01-06T02:49:15+09:00
Laravelコードを見直す
すみませんが、日本語訳はあとにします。
After reviewing the code for the new team members, I found long/wrong codes. I write it down here.Content
- 1. Use default params when getting value from request
- 2. Use Eloquent when function
- 3. Use Eloquent updateOrCreate
- 4. Use map instead of bundle if/else
- 5. Use collection when you can
- 6. Stop calling query in loop
- 7. Stop printing raw user inputted values in blade.
- 8. Be careful when running javascript with user input.
- 9. Stop abusing Morph
1. Use default params when getting value from request
The original code
$search_type = SearchTypeConstant::TITLE; if($request->has('search_type')){ $search_type = $request->get('search_type'); }It is verbose
Refactoring
$search_type = $request->get('search_type', SearchTypeConstant::TITLE);2. Use Eloquent when function
The original code
$query = User::active(); if($first_name = $request->get('first_name')){ $query = $query->where('first_name', $first_name); } $users = $query->get();We can remove temp $query variable
Refactoring
$users = User::active()->when($first_name = $request->get('first_name'), function($first_name){ return $q->where('first_name', $first_name); })->get();3. Use Eloquent updateOrCreate
The original code
if($user->profile){ $user->profile->update($request->get('profile')->only('phone', 'address')); } else { $user->profile()->create($request->get('profile')->only('phone', 'address')); }It is verbose
Refactoring
$user->profile()->updateOrCreate([], $request->get('profile')->only('phone', 'address'));4. Use map instead of bundle if/else
The original code
function get_status_i18n($status){ if($status == STATUS::COMING){ return 'coming'; } if($status == STATUS::PUBLISH){ return 'publish'; } return 'draft'; }It is long
Refactoring
private const MAP_STATUS_I18N = [ STATUS::COMING => 'coming', STATUS::PUBLISH => 'publish' ]; function get_status_i18n($status){ return self::MAP_STATUS_I18N[$status] ?? 'draft'; }5. Use collection when you can
The original code
function get_car_equipment_names($car){ $names = []; foreach($car->equipments as $equipment){ if($equipment->name){ $names[] = $equipment->name; } } return $names; }It is long
Refactoring
function get_car_equipment_names($car){ return $car->equipments()->pluck('name')->filter(); }6. Stop calling query in loop
Ex1:
The original code
$books = Book::all(); // *1 query* foreach ($books as $book) { echo $book->author->name; // Make one query like *select \* from authors where id = ${book.author_id}* every time it is called }N + 1 queries problem
Refactoring
$books = Book::with('author')->get(); // Only two queries will be called. // select * from books // select * from authors where id in (1, 2, 3, 4, 5, ...) foreach ($books as $book) { echo $book->author->name; }Simple implementation of $books = Book::with('author')->get() code as the bellow.
$books = Book::all(); //*1 query* $books_authors = Author::whereIn('id', $books->pluck('author_id'))->get()->keyBy('author_id'); // *1 query* foreach($books as $book){ $books->author = $books_authors[$book->author_id] ?? null; }Ex2;
The original code
$books = Book::all(); // *1 query* foreach($books as $book){ echo $book->comments()->count(); // Make one query like *select count(1) from comments where book_id = ${book.id}* every time it is called }N + 1 queries problem
Refactoring
=>
php
$books = Book::withCount('comments')->get();
// Only two queries will be called.
foreach($books as $book){
echo $book->comments_count;
}
Simple implementation of $books = Book::withCount('comments')->get() code as the bellow.
$books = Book::all(); // *1 query* $books_counts= Comment::whereIn('book_id', $books->pluck('id'))->groupBy('book_id')->select(['count(1) as cnt', 'book_id'] )->pluck('book_id', cnt); // *1 query* foreach($books as $book){ $book->comments_count = $likes[$book->id] ?? 0; }Total: 2 queries
Note: Read more about eager-loading
In some frameworks, like phoenix, lazy-load is disabled by default to stop incorrect usage.7. Stop printing raw user inputted values in blade.
The original code
<div>{!! nl2br($post->comment) !!}</div>There is a XSS security issue
Refactoring
<div>{{ $post->comment }}</div>Note: if you want to keep new line in div, you can use **white-space: pre-wrap**
Rules: Don't use printing raw({!! !}}) with user input values.8. Be careful when running javascript with user input.
The original code
function linkify(string){ return string.replace(/((http|https)?:\/\/[^\s]+)/g, "<a href="%241">$1</a>") } const $post_content = $("#content_post"); $post_content.html(linkify($post_content.text()));There is a XSS security issue.
Easy hack with input ishttp:deptrai.com<a href="javascript:alert('hacked!');">stackoverflow.com</a>
orhttp:deptrai.com<img src="1" alt="image">
Refactoring
function linkify(string){ return string.replace(/((http|https)?:\/\/[^\s]+)/g, "<a href="%241">$1</a>") } const post = $("#content_post").get(0); post.innerHTML = linkify(post.innerHTML)Bonus: simple sanitize function
Note: Don't use unescaped user input to **innerHTML. Almost javascript frameworks will sanitize input before print it into Dom by default.
So, be careful when using some functions like **react.dangerouslySetInnerHTML* or jquery.append() for user inputted values.*In my test results, WAF(Using our provider's default rules) can prevent server-side XSS quite well, but not good with Dom-Base XSS.
Rules: Be careful when exec scripts that contains user input values.
9. Stop abusing Morph
When using morph, we cannot use foreign key relationship and when the table is grow big we will face performance issues.
The original code
posts id - integer name - string videos id - integer name - string tags id - integer name - string taggables tag_id - integer taggable_id - integer taggable_type - stringRefactoring
posts id - integer name - string videos id - integer name - string tags id - integer name - string posts_tags tag_id - integer post_id - integer videos_tags tag_id - integer video_id - integerOriginal post is: https://khoinv.com/post/639439824292593665/write-better-laravel-code
- 投稿日:2021-01-06T02:37:59+09:00
Laravel6以降のbootstrapの導入方法
- 投稿日:2021-01-06T00:53:20+09:00
LaravelでFormRequestを使ってバリデーションする方法
Controllerが肥大化(MVCの宿命)してしまうのを少しでも解決するために、バリデーションはFormRequestを使って処理するのが良さそうだったので、自分用のメモとして使い方をまとめてみた。
FormRequestとは
バリデーションルールを外部クラス(FormRequestクラス)にまとめることができ、任意の処理で呼び出すことができる
FormRequestを下記のようにDIしてあげると、バリデーションが通った時だけコントローラー内の処理が走るようになります
このようにバリデーションを外部クラスで処理することでControllerでは自分の処理に専念することができるpublic function store(StoreRequest $request) { // バリデーションが通ったときにここの処理が走る }使ってみる
artisan
コマンドで作成できるphp artisan make:request StoreRequestStoreRequest.phpnamespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class StoreRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; // デフォルトはfalse(アクセス権限を付けない場合はtrueにする) } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ // ここにバリデーションルールを書いていく ]; } }バリデーションルール
public function rules() { return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]; }こんな感じで書く
バリデーションエラー時のレスポンスをjsonに変更
デフォルトではレスポンスがHTMLのためAPI開発などでも扱いやすいようjsonに変更する
バリデーションエラー時に実行されるfailedValidation
メソッドをオーバーライドするprotected function failedValidation(Validator $validator) { $res = response()->json([ 'status' => 400, 'errors' => $validator->errors(), ],400); throw new HttpResponseException($res); }他にも色々な機能がある
- 投稿日:2021-01-06T00:05:33+09:00
Laravelマイグレーションをやり直す方法
php artisan migrateを実行したけど、やり直したい時は、
php artisan migrate:rollbackで一つ前の状態に戻すことができる。