/// BANGBOO BLOG ///
■16/1/4 12:00AM
Laravel

https://coinbaby8.com/laravel-php-dekirukoto.html
https://laravel10.wordpress.com/
https://paiza.jp/works/laravel/primer

ルーティング:  URLと対応 .phpで終わせず処理をひとつのファイルやクラスにまとめる
Blade:  テンプレートエンジン
ミドルウェア: URL前後に処理を付加
エラーハンドラ: 例外やエラーが発生した際の処理
DI : サービスコンテナというのを用いクラスインスタンスの依存度を極力下げて、実行時に外部から与えてもらえるように
バリデーション:  入力値のチェックを設定のみで自動的に
データベース連携
認証の自動化

■PHPフレームワーク Laravel入門 サンプルファイルのダウンロード
https://www.shuwasystem.co.jp/support/7980html/5258.html

■XAMPP 7.4.1 インスコ(X-platform/Apache/MySQL/PHP/Perl)
 https://www.apachefriends.org/jp/index.html
 Win用Exeでインスコ C:\xampp\htdocs ← http://localhost/
 xamppコンパネのショートカットをデスクトップに作成
Composer PHPのパッケージ管理システム
 https://getcomposer.org/download/
 Win用Exeで通常(devでなく)でインスコ
 PHPはC:\xampp\php\php.exe、ユーザパスはC:\xampp\php
Laravel インスコ(プロジェクト作成)
 コマンドを打つと自動でインスコ
 cd C:\xampp\htdocs
 composer create-project laravel/laravel prj_laravel --prefer-dist
 5分位掛かる

Xampp(appach, mysql)起動し、Laravelプロジェクト起動はコマンドを打つ
 cd C:\xampp\htdocs\prj_laravel
 php artisan serve
  次のURLへアクセスする http://localhost:8000/
   8000のルートはここ C:\xampp\htdocs\prj_laravel\public

http://localhost では:80
php artisan serve --port=8000 がデフォ、Xamppは80
netstat -an でリスナーを見れる(XAMPPコンパネでもOK)
C:\xampp\apache\conf\httpd.conf でDocumentRootを変更できる
 Alias / "C:\xampp\htdocs\prj_laravel\public"でエイリアス?

自由に使えるか?
1)C:\xampp\htdocs\prj_laravel\public\direct_test1.php を置くとhttp://localhost:8000/direct_test1.php でPHPが動くページにアクセスできる
2)ビューで自由にPHPを動かせるか?→例えばtest.blade.phpに<?php --- ?>でコードを書けば動く
 http://localhost:8000/_test でアクセスすれば下記ルートとビューが使われtest.blade.phpだけで動く
 routes/web.php
  C:\xampp\htdocs\prj_laravel\app\Http\Controllers  無し
  config/app.php ファサード無し サービスプロバイダ無し
  C:\xampp\htdocs\prj_laravel\app\Http\Middleware フィルタ無し
  C:\xampp\htdocs\prj_laravel\app\Http/Kernel.php ミドルウェア無し
 C:\xampp\htdocs\prj_laravel\resources\views\test.blade.php

http://localhost:8000/fun/

php artisan make:controller FunController --resource

MySQLにDB:test ID:test/testtestを設定しテーブルを作成
CREATE TABLE `fun_inquiry` (
  `no` bigint(20) NOT NULL auto_increment,
  `title` varchar(255) NOT NULL default '',
  `inquiry` text,
  `title_ex` varchar(255) NOT NULL default '',
  `inquiry_ex` text,
  `date_latest` datetime NOT NULL default '1000-01-01 00:00:00',
  `date_2ndlast` datetime NOT NULL default '1000-01-01 00:00:00',
  `date_first` datetime NOT NULL default '1000-01-01 00:00:00',
  `ip` varchar(255) NOT NULL default '',
  `flag_who` tinyint(4) NOT NULL default '0',
  `flag_active` tinyint(4) NOT NULL default '0',
  PRIMARY KEY  (`no`)
) ENGINE=InnoDB
  DEFAULT CHARACTER SET utf8
   COLLATE utf8_unicode_ci;
/*
flag_who 1:customer,2:admin
flag_active 1:有効,2:削除
*/

ダミーデータ挿入
INSERT INTO `fun_inquiry`(`title`, `inquiry`, `date_lastest`, `date_first`, `ip`, `flag_who`, `flag_active`) VALUES ('あああ', 'いいい', now(), now(), '192.168.56.1', '2', '1')

.env とconfig/database.phpに DB情報を記入
 変更後は php artisan config:cache

C:\xampp\htdocs\prj_laravel\routes\web.php にコントローラ渡し記述
 index crate store show($id) edit($id) update(Request $request, $id) destroy($id)
C:\xampp\htdocs\prj_laravel\app\Http\Controllers\FunController.php にView渡し記述
C:\xampp\htdocs\prj_laravel\resources\views\index.blade.php など
 index crate store show edit update destroyのViewファイルを作成し リンク設定
 href=/でルートからリンクを指定
 パスはルート/が安定する、相対だと理由不明で階層が崩れる事があった、Form methodを入れる
 
情報を表示させたい
 コントローラに
 $param = ['items' => $items,
  'msg' => 'データを追加しました',
  'request' => $request,
  'response' => $response];
 return view('index', $param);
 ビューに
 <?php dump($items);
 dump($msg);
 dump($request);
 dump($response); ?>

バリデーションするのに同カラム保持のpostsテーブルが必要であったので作成(多分ダミー)
テーブル作成方法: php artisan make:model posts をCMDしSQL流し込み
CREATE TABLE `posts` (
  `no` bigint(20) NOT NULL auto_increment,
  `title` varchar(255) NOT NULL default '',
  `inquiry` text,
  `title_ex` varchar(255) NOT NULL default '',
  `inquiry_ex` text,
  `date_latest` datetime NOT NULL default '1000-01-01 00:00:00',
  `date_2ndlast` datetime NOT NULL default '1000-01-01 00:00:00',
  `date_first` datetime NOT NULL default '1000-01-01 00:00:00',
  `ip` varchar(255) NOT NULL default '',
  `flag_who` tinyint(4) NOT NULL default '0',
  `flag_active` tinyint(4) NOT NULL default '0',
  PRIMARY KEY  (`no`)
) ENGINE=InnoDB
  DEFAULT CHARACTER SET utf8
   COLLATE utf8_unicode_ci;
エラー後は元のページに戻るが元の値とエラーを出すには
 <input name="title" value="{{old('title')}}">
 @if($errors->has('title'))
    @foreach($errors->get('title') as $message)
     <br><red>{{ $message }}</red>
    @endforeach
 @endif
//バリデーション例
https://qiita.com/fagai/items/9904409d3703ef6f79a2
https://blog.capilano-fw.com/?p=341#digitsdigits_between
$validate_rule = [
    'title' => 'required|unique:posts|max:255',
    'inquiry' => 'required',
    'title_ex' => 'required|unique:posts|max:255',
    'inquiry_ex' => 'required',
    'date_latest' => 'required|date_format:Y-m-d H:i:s',
    'date_2ndlast' => 'date_format:Y-m-d H:i:s',
    'date_first' => 'required|date_format:Y-m-d H:i:s',
    'ip' => 'required|ip',
    'flag_who' => 'required|integer|between:1,2',
    'flag_active' => 'required|integer|between:1,2'];
$this->validate($request, $validate_rule);

自分で命名したデータベースをLaravelに参照させたいときは、テーブル名をモデルファイル内で指定しなければいけない
php artisan make:model DbTblFunInquiry をCMDし
app/DbTblFunInquiry.php を編集
namespace App;
use Illuminate\Database\Eloquent\Model;
class DbTblFunInquiry extends Model{
    protected $table = 'fun_inquiry';
}

コントローラでDB追加
$now = date('Y-m-d H:i:s');
$input = ['title' => $request->title,
    'inquiry' => $request->inquiry,
    'date_latest' => $now,
    'date_first' => $now,
    'ip' => $request->ip(),
    'flag_who' => '1',
    'flag_active' => '1'];
$insert = DB::insert('insert into fun_inquiry(title, inquiry, date_latest, date_first, ip, flag_who, flag_active) values (:title, :inquiry, :date_latest, :date_first, :ip, :flag_who, :flag_active)', $input);

DB操作で同じパラメータを使おうとしてもSQLで数が合わないとだめで$input作り直した
→エラーはrequest/responseインスタンスをダンプしキッチリと見ると原因がわかる
$update = DB::update('update fun_inquiry set
        title = :title,
        inquiry = :inquiry,
        title_ex = :title_ex,
        inquiry_ex = :inquiry_ex,
        date_latest = :date_latest,
        date_2ndlast = :date_2ndlast,
        ip = :ip,
        flag_who = :flag_who,
        flag_active = :flag_active
        where no = :no', $input);
$input = ['no' => $request->no];
$items = DB::select('select * from fun_inquiry where no = :no', $input);



■ララベルの基本
gitの管理対象から外す
.gitignore ファイルに追加
環境毎に変更したい情報
.env ファイルにステージング環境、本番環境の情報をまとめる
設定情報は下記以下、DB接続/メール/サービスプロバイダ登録/ファサード登録等
config/
バリデーションメッセージの日本語化
resources/lang/ja/validation.php

===============

├── app
│   └── Article.php
│   └── Http
│       └── Controllers
├── resources
│   └── views
└── routes

//基本構成
 routes/web.php
 C:\xampp\htdocs\prj_laravel\app\Http\Controllers ある程度処理をする?モデル化?
 config/app.php ファサード サービスプロバイダ
 C:\xampp\htdocs\prj_laravel\app\Http\Middleware フィルタ
 C:\xampp\htdocs\prj_laravel\app\Http/Kernel.php ミドルウェア
 C:\xampp\htdocs\prj_laravel\resources\views\test.blade.php
CRUDはリソースコントローラで一気作成ができる
 php artisan make:controller PostsController --resource
モデルを作成cmd EloquentでDB接続 app/Article.php
 php artisan make:model Article
ファサード
 対象となるクラスの作成 class Payment {
 サービスプロバイダの作成 php artisan make:provider PaymentServiceProvider
 サービスコンテナに paymentキーでPaymentクラスをバインド
 config/app.php に PaymentServiceProviderと エリアスを追加
認証用ビューとルートを作成(認証機能はプロジェクト作成時点で既成)
 php artisan make:auth

//php artisanコマンド(独自作成も可)
何ができるかリスト確認cmd
php artisan list
使い方確認cmd
php artisan help コマンド名
cmdでララベル機能が使える
php artisan tinker
古いパッケージをチェック
composer update --dry-run
古いパッケージをチェック(グローバルの場合)
composer global update --dry-run

.env変更後は
php artisan config:cache
コントローラを作成cmd
php artisan make:controller WelcomeController
モデルを作成cmd
php artisan make:model Article
Form Request を生成
php artisan make:request ArticleRequest
CRUDはリソースコントローラで一気作成ができる
php artisan make:controller PostsController --resource
認証用ビューとルートを作成(認証機能はプロジェクト作成時点で既成)
php artisan make:auth
ルーティングのリストを表示
php artisan route:list

//jsファイルやcssファイルはpublicフォルダ配下に入れる、HTMLやIMGも
 public
   |__ css
   |__ js
   |__ img
   |__ html
 asset('ファイルパス')はpublicディレクトリのパスを返す関数、SSLならsecure_assset()
 <script src="{{ asset(mix('js/app.js')) }}" defer></script>
 <link href="{{ asset(mix('css/app.css')) }}" rel="stylesheet">
 <img alt="ロゴ" src="{{ asset('/img/logo.png') }}">
 url()で完全なURL、SSLならsecure_url( )
 .hoge::before { background: url("../img/hoge.svg") no-repeat center center; }
 レスポンスでHTMLファイルを返したい時
 Route::get('fun/html', function() {
     return File::get(public_path() . '/html/test_direct.html');
 });

//ルーティング routes/web.php
必須パラメータ(値がないとエラー)
 Route::get('login/{id]/{pass}', function($id, $pass){
任意パラメータ
 Route::get('login/{id?]/{pass?}', function($id='noname', $pass='unknown'){
 login/taro/aaa でアクセスするときRouteにlogin/taro/{pass?}とlogin/{id?]/{pass?}があればどうなる?
  通常 http://url/コントローラ/アクション と設計され Route::get('パス', 'コントローラ@アクション' (アクションはコントローラ内のメソッド)
Route::get('/', 'WelcomeController@index');
Route::get('contact', 'WelcomeController@contact');
正規表現を配列にして、複数指定
Route::get('foo/{id}/{name?}', function($id, $name = 'init') {
    return "パラメータ $id, $name";
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
下のようにnamed rootで名付けも
Route::get('articles', 'ArticlesController@index')->name('articles.index');
Route::get('articles/{id}', 'ArticlesController@show');
Route::post('articles', 'ArticlesController@store')->name('articles.store');

//コントローラ app/Http/Controllers/WelcomeController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class WelcomeController extends Controller{
    public function index(){
        return view('welcome');
    }
    public function contact(){
        return view('contact');
    }
}
class ArticlesController extends Controller {
    public function create() {
        return view('articles.create');
    }
    public function store(ArticleRequest  $request) {
 //コントローラはメソッドの引数にタイプヒントでクラスを記述するとインスタンスを自動生成して渡してくれます
        //$inputs = \Request::all(); //フォームの入力値を取得
        //dd($inputs); //デバッグ: $inputs の内容確認
        Article::create($request->validated());//マスアサインメントで記事をDBに作成
        return redirect()->route('articles.index')->with('message', '作成!'); //記事一覧へリダイレクト
    }
}

//ビュー resouces/views/welcome.blade.php
resources/view/contact.blade.php
view('aaa')の場合、ビューはaaa.blade.phpが読まれる、なければaaa.php
blade では PHP で評価した結果を表示する場合、ダブルカーリー {{ }}
{{!! XXXX !!} はPHP評価をエスケープ処理を行わず表示(htmlタグがでる)、{{ XXXX }} のエスケープ(htmlタグがでない)
@csrfでCSRFトークン(hidden)を挿入
{{-- コメント文 --}}

ビューテンプレ
親ビューですべきこと
 大まかレイアウトを記述し、各子ビュー固有のパーツをどこの@yieldで反映させたいかを指定
 <html><body>
 @yield('body')
 </html>
子ビューですべきこと
 @extendsで親ビュー継承、@sectionで自分固有のパーツを定義
@extends('common.layout')//resources/views/common/layout.blade.php dotはフォルダの事
@section('body')
    <p>表示したい内容<p>
@endsection

@extends('layout')
 <h1>About Me: {{ $first_name }} {{ $last_name }}</h1>
    {{-- エラーの表示を追加 --}}
    @if ($errors->any())
        <div class="alert alert-danger">
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif
 {!! Form::open() !!}
<div class="form-group">
    {!! Form::label('title', 'Title:') !!}
    {!! Form::text('title', null, ['class' => 'form-control']) !!}
</div>
<div class="form-group">
    {!! Form::label('body', 'Body:') !!}
    {!! Form::textarea('body', null, ['class' => 'form-control']) !!}
</div>
<div class="form-group">
    {!! Form::label('published_at', 'Publish On:') !!}
    {!! Form::input('date', 'published_at', $published_at, ['class' => 'form-control']) !!}
</div>
<div class="form-group">
    {!! Form::submit($submitButton, ['class' => 'btn btn-primary form-control']) !!}
</div>

 大元のレイアウトビュー resouces/views/layout.blade.php
 <!DOCTYPE HTML>
 <html lang="ja">
 <body>
       @if (session('message'))
            <div class="alert alert-success">{{ session('message') }}</div>
        @endif
     @yield('content')
 </body>
 </html>

変数を渡す場合
ビューファサードは第二引数へ配列でパラメータを渡す
Route::view('sample/view', 'sampleview', ['number' => 123456789]);
コントローラでは配列で渡す
public function create(Request $request, Response $response){
    $items = DB::select('select * from fun_inquiry');
    $param = ['items' => $items,
    'msg' => 'データを追加するにはログインが必要です',
    'request' => $request,
    'response' => $response];
    return view('index', $param);
他にはcompact関数又はwithメソッドで送信
public function test() {
    $test_1 = "テスト1";
    $test_2 = "テスト2";
    return view('test.normal',compact('test_1','test_2'));
}
public function test() {
    $test_1 = "テスト1";
    $test_2 = "テスト2";
    return view('test.normal')->with([
       "test_1" => "テスト1",
       "test_2" => "テスト2",
    ]);
}
フォーム
    <input type="text" name="textbox" value="">
    <input type="radio" name="radio-btn" value="選択1">
    <input type="radio" name="radio-btn" value="選択2">
    テキストボックスは{{$post_data['textbox']}}
    ラジオボタンは{{$post_data['radio-btn']}}
処理系ディレクティブ
@isset($msg) {{$msg}} @else 今何してる? @endisset
@for($i=1;$i<10;$i++;) {{$i}} @endfor
@while($msg=='') 今何してる? @endwhile
@break @continueも
        @foreach($db_data as $recode)
           {{$recode->id}}
           {{$recode->name}}
           {{$recode->old}}
        @endforeach
        {{$db_data[0]->name}} 0番目
        $count = $db_data->count(); カウント
        $avg = $db_data->avg('old'); 平均

//モデル app/Article.php
DBとモデルオブジェクトを対応付ける機能を Eloquent という
Eloquent の機能を継承しビジネスロジックを加えたクラスがモデル
ORM(Object-relational mapping)でDBアクセスをする
 $items = DbTblFunInquiry::all(); //モデルを作成しているのでコレでDBアクセス可
 $items = DbTblFunInquiry::where(no, '>=', 3)->get();
Eloquentのリレーションには、1対n、1対1やn対n等が
$this->hasMany('App\Article');
$this->belongsTo('App\User');

//バリデーション
エラーが出て前のページに戻る
public function update(Request $request, Response $response){
        $validate_rule = [
            'title' => 'required|unique:posts|max:255',
            'inquiry' => 'required'];
        $this->validate($request, $validate_rule);

作り方、まずコントローラでは
    $upfileRequest = new upfileRequest();
    $rules = $upfileRequest->rules();
    $errorMsg = $upfileRequest->messages();
    $result = $this->validate($request, $rules, $errorMsg);
cmdでリクエストクラスを生成
$ php artisan make:request upfileRequest
下記のように
class upfileRequest extends Request{
    public function rules(){
        return [
            'title'                => 'required',
            'address'              => 'required',
            'url'                  => 'url',
            'img'                  => 'file|image|mimes:jpeg,jpg,png,gif|max:' . config('const.validate_limits.upload_image_size'),
            'img_update_flag'      => 'sometimes|accepted',
        ];
    }
    public function messages(){
        return [
            'title.required'               => 'タイトルは必須です',
            'address.required'             => '住所は必須です',
            'url.url'                      => 'このURLは不正です(http://またはhttps://から記述してください)',
            'img.mimes'                    => '選択できる画像はJPEG・GIF・PNG形式のみです',
            'img.max'                      => '1MB以下のファイルを選択してください',
            'img_update_flag.accepted'     => '1MB以下のファイルを選択してください',
        ];
    }
}

//ミドルウェアでフィルタリングを実行 
記事の作成や編集、削除はログインしていないと実行出来ないように制限する等
app/Http/Middleware/ にミドルウェアがある(vendor/laravel/framework 以下にも多数ある様)
 ├── CheckForMaintenanceMode.php
 ├── EncryptCookies.php
 ├── RedirectIfAuthenticated.php
 ├── TrimStrings.php
 ├── TrustProxies.php
 └── VerifyCsrfToken.php
app/Html/Kernel.php の中でミドルウェアをシステムに登録
1)グローバルミドルウェア(全ての HTTP リクエストに適用)
2)ルートミドルウェア(Routeで使用)
3)グループ(Web等)で登録可
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
ミドルウェアの使用
 routes/web.phpで定義したルートには全てwebミドルウェアが適用される
 ルートミドルウェアを使う場合(Routeに配列で登録)
  Route::get('admin/profile', function () {
      //処理
  })->middleware('auth', '2つ目', ...);
 // ホーム(ログインしていないと見れないよう)
 Route::group(['middleware' => 'auth'], function() {
     Route::get('/home', function () {
         return view('home');
     });
 });
 コントローラで使う場合
   ログインをしていなくてもindexとshowの表示が可能
  class ArticlesController extends Controller{
      public function __construct(){
          $this->middleware('auth')
              ->except(['index', 'show']);
      }
 ビューでの使用
  @auth
  ログインしています
  @endauth
Middleware の新規作成(基本的なものは元々ある)
 php artisan make:middleware MyMiddleware
 登録確認メールの確認が済んでいるかミドルウェアで
  https://laravel10.wordpress.com/2015/05/05/laravel5

//ファサード
サービスコンテナで利用可能なクラスへのstaticインターフェースを提供
サービスコンテナからオブジェクトへのアクセスを提供するクラス
Hoge::ぶらぶら とシンプルにコードが書けるようになる
Route::get('/', 'WelcomeController@index'); はファサードがあり実際は下記
$this->app->make('router')->get('/', 'WelcomeController@index');
<?php namespace Illuminate\Support\Facades;
class Route extends Facade {
    protected static function getFacadeAccessor() {
        return 'router';
    }
}
実際に作成↓
対象となるクラスの作成 class Payment {
サービスプロバイダの作成 php artisan make:provider PaymentServiceProvider
サービスコンテナに paymentキーでPaymentクラスをバインド
class PaymentServiceProvider extends ServiceProvider {
    public function boot(){  }
    public function register()    {
        $this->app->bind(
        );
    }
}
config/app.php に PaymentServiceProviderを追加
ファサードクラスを作成
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Payment extends Facade {
    protected static function getFacadeAccessor() {
        return 'payment';
    }
}
config/app.php に Paymentファサードのエリアスを追加
    'aliases' => [
        'Payment' => App\Facades\Payment::class,
    ],
app/Http/route.php 内で Paymentファサードを使用
Route::get('pay/{money}', function($money){
    return \Payment::pay($money);
})->where('money', '[0-9]+');
この場合はここへアクセス http://localhost:8000/pay/10000
            'payemnt', 'App\Services\Payment'

//セッションはキーと値 https://qiita.com/reflet/items/5638ab18fd7cededed17
$requet->session()->put(key, value); 値を保存
$v = $request->session()->get(key); 値を取得
$request->session()->flush(); セッション破棄 session()->forget('key');個別
$request->session()->all(); 全取得

//クッキー
\Cookie::queue('testcookie', "aaa", 60); 書き込み60分
$test = \Cookie::get('testcookie');取得
setcookie('testcookie');削除

//httpヘッダー
以下を入れて見れる
Controllerで
use Illuminate/http/Request;
use Illuminate/http/Response;
public function create(Request $request, Response $response){
        $param = ['msg' => 'データを追加します',
            'request' => $request,
            'response' => $response];
        return view('create', $param);
Viewで
<?php
dump($msg);
dump($request);
dump($response);
?>
下記メソッドも
$request->url() クエリ変数がつかないURL
$request->fullUrl() クエリ変数つきフルURL
$request->path() ドメインがないパス部分
$response->status()
$response->content()
$response->setContent(値)

//認証
https://reffect.co.jp/laravel/laravel-authentication-understand
https://qiita.com/apricotcomic/items/d7407d4b12f41e2ff5ed
http://recipes.laravel.jp/recipe/64
https://takahashi-it.com/php/laravel54-auth-customize/
https://saba.omnioo.com/note/6246/laravel6-0-makeauthが無くなった/

基本ページと認証仕組みをcmdで入れ込む
 (users/pw_resetsテーブル等はmigrationで作成しておく必要)
 php artisan make:auth はv6から使えない様
 vue省略型、React性能高
composer require laravel/ui
php artisan ui vue --auth
 Nodeを入れて下記が必要のようだが(管理者権限が要るCMD)
cd C:\Program Files\nodejs
npm init --yes
npm install
npm run dev
 下記ページができる
http://localhost:8000/login
http://localhost:8000/register
http://localhost:8000/password/reset
 ルーティング関連(コントローラ)
 C:\xampp\htdocs\prj_laravel\app\Http\Controllers\HomeController.php
 C:\xampp\htdocs\prj_laravel\app\Http\Controllers\Auth
 ルーティングAuth::routes();から下記へ
  C:\xampp\htdocs\prj_laravel\vendor\laravel\framework\src\Illuminate\Support\Facades\Auth.php
  C:\xampp\htdocs\prj_laravel\vendor\laravel\framework\src\Illuminate\Routing\Router.php
  C:\xampp\htdocs\prj_laravel\vendor\laravel\framework\src\Illuminate\Foundation\Auth
 migration するとデータは消えるのか? php artisan migrate ではDropされない、migrate:flushだと消える
 Auth自動生成ページ系の遷移設定
  protected $redirectTo = RouteServiceProvider::HOME;
   HOMEは定数(C:\xampp\htdocs\prj_laravel\app\Providers\RouteServiceProvider.php で
    public const HOME = '/fun';に設定
  コントローラ類に下記を追加することで制御が可能だがコントローラを分ける必要がありそう
  public function __construct(){ $this->middleware('auth'); }
  public function __construct(){ $this->middleware('guest'); }
  public function __construct(){ $this->middleware('guest')->except('logout'); }
ビューでログイン判定可
<?php
$user = \Auth::user();
if ($user) {
    echo 'ログイン中: ' . $user->name;
?>
  | <a href={{ route('logout') }} onclick="event.preventDefault();document.getElementById('logout-form').submit();">ログアウト</a>
  <form id='logout-form' action={{ route('logout')}} method="POST" style="display: none;">
    @csrf
</form>
<?php
}else{
?>
<a href="/login">Login</a>  |
<a href="/register">Register</a>
<?php
}
?>
ビューにこんな感じでも
 @if(Auth::check())
     {{ Auth::id() }}
 @endif
 @auth
   <p>ログインユーザーに表示する。</p>
 @guest
   <p>ログインしていないユーザーに表示する。</p>
 @endguest
編集機能ページ等はコントローラでログイン判定しMSG表示とページ変遷(ログインするとCreateとUpdate可)
    public function edit(Request $request, Response $response)
    {
        $user = \Auth::user();
        if ($user) {
            $input = ['no' => $request->no];
            $items = DB::select('select * from fun_inquiry where no = :no', $input);
            $param = ['items' => $items,
                'msg' => '#' . $request->no . 'を編集できます',
                'request' => $request,
                'response' => $response];
            return view('edit', $param);
        }else{
            $input = ['no' => $request->no];
            $items = DB::select('select * from fun_inquiry where no = :no', $input);
            $param = ['items' => $items,
                'msg' => '#' . $request->no . 'を編集するにはログインが必要です',
                'request' => $request,
                'response' => $response];
            return view('show', $param);
        }
    }
コントローラでユーザ判定
 use Illuminate\Support\Facades\Auth;
 class モデル名Controller extends Controller
 {
     public function ブレード名(){
         $auths = Auth::user();  // ログイン中のユーザー情報
         $user_no = Auth::id();  // ユーザーIDのみ
         if (Auth::check()) {
          if($a==1){ Auth::logout(); }
ログアウト時の変遷
logoutメソッドの本体は、AuthenticatesUsers にあるのでHOMEへ変更
 C:\xampp\htdocs\prj_laravel\vendor\laravel\framework\src\Illuminate\Foundation\Auth
 //return $this->loggedOut($request) ?: redirect('/');
 use App\Providers\RouteServiceProvider;
 return $this->loggedOut($request) ?: redirect(RouteServiceProvider::HOME . '?fr=logout');
https://takahashi-it.com/php/laravel-login-authenticated-override/
/vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php の
中に、ログイン処理のリダイレクト直前で実行される authenticated() メソッドが書かれているが空で
LoginController.php でオーバーライドする形で実装
 use Illuminate\Foundation\Auth\AuthenticatesUsers;
 protected function authenticated()
 {
     Session::put('session_start_time', date('H:i'));
 }

Session にログイン時間を入れる
 use Session;//これは場合によって不要なようである
 Session::put('session_start_time', date('H:i'));
 $session_start_time = Session::get('session_start_time');
 @if (Session::has('session_start_time'))
  {{ Session::get('session_start_time') }}
 @endif
Authと連動しログアウトするとSessionがなくなる

//サービスプロバイダー ServiceProvider
 処理や表示したいビューを登録しておける(初期処理用、カウンターのようにインクリできない)
 クラスを作りboot()に付与して、例えばいつでもその処理とメッセージをビューに呼び出す
 MVCでビューにビジネスロジックを書く必要がある矛盾を解消するもの?

クラス作成 C:\xampp\htdocs\prj_laravel\app\Http\Gadget\Lucky.php
namespace App\Http\Gadget;
class Lucky
{
    public $count = 0;
    public $rate = 0;
    public function addupCounter(){
        $this->count++;
    }
    public function getCounter(){
        $this->count++;
        return $this->count;
    }
    public function getLucky(){
        $this->rate = rand(1, 100);
        return $this->rate;
    }
}
サービスプロバイダの作成 php artisan make:provider GadgetServiceProvider
 app/Providers/AppServiceProvider.php に追記でもいい
config/app.php に GadgetServiceProviderを追加
GadgetServiceProviderのbootに追記
    public function boot(){
        \View::composer(
            'index', function($view){
                $LuckyInstance = new \App\Http\Gadget\Lucky;
                $view->with('lucky', $LuckyInstance->getLucky());
            });
index.bladeのビューに追記 {{ $lucky }}

//ユニットテスト(Laravel PHPunit)
リファレンス https://readouble.com/laravel/5.7/ja/http-tests.html#assert-see
 https://qiita.com/rev84/items/12fbd16d210d6a86eff9
処理を全部書かないと駄目か https://qiita.com/shindex512/items/4f28f8e06ef2d10e8d2b
PHPUnitが入っていてTestsフォルダに
TestCaseクラスにあるメソッドはtestで始まる名前のメソッドと判断し実行する、testABC()とか
DB_DATABASEが使用されるのでDBに用意する、設定等
database/factoriesにダミーデータ作成ファクトリーがある、必要な形式のデータが作られるよう調整する
 $factory->define(App\User::class, function (Faker\Generator $faker) {
     static $password;
     return [
         'name' => $faker->name,
         'email' => $faker->unique()->safeEmail,
         'password' => $password ?: $password = bcrypt('secret'),
         'remember_token' => str_random(10),
     ];
 });
 $factory->define(App\Person::class, function (Faker\Generator $faker) {
     return [
         'name' => $faker->name,
         'mail' => $faker->safeEmail,
         'age' => random_int(1,99),
     ];
 });
テストクラス作成 php artisan make:test LuckyTest
use Tests\TestCase;
use App\Http\Gadget\Lucky;
class LuckyTest extends TestCase{
    public function testLucky(){
        $response = $this->get('/fun');
        $response->assertStatus(200);
  $lucky = new Lucky;
  $lucky_num = $lucky->getCounter();
  $response->assertGreaterThanOrEqual(1, $lucky_num);
  $response->assertLessThanOrEqual(100, $lucky_num);
 }
}
 use DatabaseMigration; を入れておくとテスト前後で自動でロールバックするらしい
テスト実行cmd vendor\bin\phpunit

//ページネーション
コントローラ側: $items = DB::table('fun_inquiry')->Paginate(7);
ビュー側: {{ $items->links() }} 合計{{ $items->total() }}
CSS側:
 ul.pagination { text-align: center; margin: 0; padding: 0; }
 .pagination li { display: inline; margin: 0 2px; padding: 0; display: inline-block; background:#eeeeee; width: 25px; height: 25px; text-align: center; position: relative; }
 .pagination li a:hover, .pagination li a.active{ color: #000; background: #ccf; }
 下記ではオブジェクトではなく配列を返しておりエラー
  $items = DB::select('select * from fun_inquiry')->simplePaginate(7);
 下記のようにクエリビルダかエロクエントならばOKのよう
  $users = DB::table('users')->paginate(15); //query builder example
  $users = User::where('votes', '>', 100)->paginate(15); //eloquent example
  $users = DB::table('users as u')->select(['u.email','u.name','u.created_at'])->whereRaw("u.id in (select max(ui.id) from users ui where ui.status = 0)")->orderBy("ui.id","desc")->paginate(3)
吐き出されるソース resources/views/pagination/default.blade.phpで修正可能
 <nav>
 <ul class="pagination">
 <li class="page-item disabled" aria-disabled="true" aria-label="pagination.previous">
 <span class="page-link" aria-hidden="true">&lsaquo;</span>
 </li>
 <li class="page-item active" aria-current="page"><span class="page-link">1</span></li>
 <li class="page-item"><a class="page-link" href="http://localhost:8000/fun?page=2">2</a></li>
 <li class="page-item">
 <a class="page-link" href="http://localhost:8000/fun?page=2" rel="next" aria-label="pagination.next">&rsaquo;</a>
 </li>
 </ul>
 </nav>

===============
■その他の機能
オートローダー
 require "vendor/autoload.php";
 autoload設定はcomposer.json を修正
ロガー
 Monolog\Logger
エラーログ
 storage/logs/*.log
メール送信
 Mail::raw()
 Laravel 5.3 からは artisan make:mail でMailable の派生クラス
  php artisan make:mail Hello
  app/Mail/Hello.php が作成される、Viewで本文
  Mail::to('reciver@example.com')->send(new Hello($name));
データベース管理(migrationファイルを作成→migration実行)
 php artisan make:migration create_users_table --create=users
 php artisan migrate
  database/migrations/2014_10_12_000000_create_users_table.php ができる
  php artisan migrate:rollback でロールバック
Bootstrap
 靴紐のソースを npm でダウンロードしLaravel Mixで public に配置
デバッガー
 composer require barryvdh/laravel-debugbar でインスコ
  config/app.php のサービス・プロバイダーとファサードに以下の設定を追加
  Barryvdh\Debugbar\ServiceProvider::class,
  'Debugbar' => Barryvdh\Debugbar\Facade::class,
デバッグ
 dd($articles->first());  // dump and die ダンプして停止
   Log::debug("incoming: GET hello");
   Log::info("incoming: GET hello");
   Log::warning("incoming: GET hello");
  debug,info,notice,warning,error,critical,alertの7つ、storage/logs/に出力
全件
 $articles = Article::all();
作成日の降順に記事をソート
 $articles = Article::orderBy('published_at', 'desc')->orderBy('created_at', 'desc')->get();
$articles = Article::latest('published_at')->latest('created_at')->get();
現在より以前
 $articles = Article::latest('published_at')->latest('created_at')->where('published_at', '<=', Carbon::now())->get();
フラッシュメッセージ
 composer require laracasts/flash
Named rootはURLでなくルーティングに別名をつける
 return redirect()->route('articles.index')
CRUDはリソースコントローラで一気作成ができる
 php artisan make:controller PostsController --resource
 // routes/web.php
 Route::resource('posts', 'PostsController')->only(['index', 'show']);
 Route::resource('posts', 'PostsController')->expect(['create', 'store', 'update', 'destroy']);
IDEでファサードの補完を行う
 composer require barryvdh/laravel-ide-helper
 php artisan ide-helper:generate
ページネーションで1ページに表示するデータ数を指定
$articles = Article::latest('published_at')->paginate(15);
 render() でページネーションリンクをビューに生成 {!! $articles->render() !!}
ダミーデータ生成
 seeder/factory/fakerで
サービスコンテナ
 インスタンス化を管理する
 サービスコンテナへクラスをバインドすると(キー名とクラスの紐付け)、クラス間の依存性を管理しやすい
 インターフェイスでバインドとタイプヒントで依存性注入
 $this->app->bind('SenderInterface', 'MailSender');
  singleton()でバインドすると何度コンテナが呼び出されても同じインスタンスが返されメモリ効率が良い
  $this->app->singleton('sender_single', 'MailSender');
 Route::get('send/{message?}', function(Messenger $messenger, $message = '合格') {
    return $messenger->send($message);
 });
シングルトン(Singleton)パターンは
 あるクラスのインスタンスを一つだけにする
 同じクラスを使って新しいオブジェクトを再度作成すると最初に作ったオブジェクトの参照になる
 function Singleton () {
   // すでにSingleton.instance が存在する場合にはSingleton.instance を返す
   if(typeof Singleton.instance === 'object') {
     return Singleton.instance;
   }
サービスプロバイダー
 config/app.php にリストアップされ起動時に読込
 Composerを使ってパッケージをインストールしたもの
 vendor/laravel/framework/src/illuminate/Auth\AuthServiceProvider等を見ると
 register()に登録を実装、boot()でそのサービス固有の初期処理を自由に実装
  作成は php artisan make:provider MessengerProvider
エリクサー
 Elixirはnode.jsのgulpをラッピングした自動化ツール
 SassをCSSに変換、複数のファイルを1ファイルに結合、ミニファイ(余計な空白や改行を削る)、ファイルの変更を監視してタスクを実行、リロード用にCSSやJavaScriptのファイル名にバージョンを付与
ファイルアップロード
 https://reffect.co.jp/laravel/how_to_upload_file_in_laravel
 https://team-lab.github.io/skillup/step2/07-upload.html
 https://laraweb.net/tutorial/2707/
 https://qiita.com/mashirou_yuguchi/items/14d3614173c114c30f02
 php.iniで定義: upload_max_filesize post_max_size等
 windowsはパーミッションの設定は不要、XamppのTMPはここ C:\xampp\tmp\
  $request->file('upfile')->getRealPath();//C:\xampp\tmp\php103B.tmp
 dump($request->file('upfile'));

vendor\symfony\http-foundation\Request.phpにまつわるもの
$request->file('file')->store($path);
$request->file('file')->storePublicly($path);
$request->file('file')->storePubliclyAs($path);
$request->file('file')->storeAs($path,$filename);
 $pathにtestならstorage/app/testに保存される

vendor\laravel\framework\src\Illuminate\Http\Request.phpにまつわるもの
$request->file('file')->move($destinationPath);
echo $request->file('upfile')->getPathname() . '<br>'; //C:\xampp\tmp\php76B8.tmp
echo $request->file('upfile')->getClientOriginalName() . '<br>'; //kanoe.jpg
echo $request->file('upfile')->getClientMimeType() . '<br>'; //image/jpeg
echo $request->file('upfile')->guessClientExtension() . '<br>'; //jpeg
echo $request->file('upfile')->getClientSize() . '<br>'; //20367
echo $request->file('upfile')->getError() . '<br>'; //0
echo $request->file('upfile')->isValid() . '<br>'; //1
echo $request->file('upfile')->getMaxFilesize() . '<br>'; //41943040
echo $request->file('upfile')->getErrorMessage() . '<br>'; //The file "kanoe.jpg" was not uploaded due to an unknown error.

こういうのもあるが、、、
echo $request->file('upfile')->getRealPath() . '<br>'; //C:\xampp\tmp\php9EA6.tmp
echo $request->hasFile('upfile') . '<br>'; //1
echo $request->file('upfile')->path() . '<br>'; //C:\xampp\tmp\php9EA6.tmp
echo $request->file('upfile')->extension() . '<br>'; //jpeg
ビューでは
 <form action="{{ url('upload') }}" method="POST" enctype="multipart/form-data">
     {{ csrf_field() }}
     <input type="file" name="upfile">
     <input type="submit">
 </form>
 @if ($errors->any())
 <ul>
     @foreach($errors->all() as $error)
     <li>{{ $error }}</li>
     @endforeach
 </ul>
 @endif
 @isset ($filename)
 <div>
 <img src="{{ asset('storage/' . $filename) }}">
 </div>
 @endisset
コントローラでは
 public function upload(Request $request){
  $this->validate($request, [
      'file' => [
          'required',
          'file',
          'image',
          'mimes:jpeg,png',
      ]//1行だと上手くいかんかった
  ]);
  if ($request->file('file')->isValid([])) {
      $path = $request->file('file')->store('public');//適当な名前でstorage/app/publicに保存
      return view('home')->with('filename', basename($path));
  } else {
      return redirect()
          ->back()
          ->withInput()
          ->withErrors();
  }
 }
 名前をつけたいのであれば
 $new_filename = 'up_' . $request->file('file')->getClientOriginalName();
 $request->file('file')->storeAs('public', $new_filename);
  storage/app/publicに保存されるからアクセスするにはpublic/storageからシンボリックリンクを張る
  cmdのphp artisan storage:link
ストレージ
 https://reffect.co.jp/laravel/laravel-storage-manipulation-master
 config/filesystems.php に設定があり
 'public' => [
     'driver' => 'local',
     'root' => storage_path('app/public'),
     'url' => env('APP_URL').'/storage',
     'visibility' => 'public',
 ],
 storage/app/publicがpublic diskになっておりファイルが保存されるが
 アクセスするにはpublic/storageからシンボリックリンクを張る必要がある
 ララベルでは/publicに通常アクセスさせるから
 php artisan storage:link
URLメソッド
 https://blog.capilano-fw.com/?p=2537
 echo url('/'); //url()の引数に空白もしくは/を指定するとトップページを取得
定数 const
 https://qiita.com/clustfe/items/4fa169c6d5114a9b6f4e
 https://hiroto-k.hatenablog.com/entry/2018/03/28/213000
 config/*.phpにenv()の値を入れてconfig()から参照
  config/const.phpを作り下記を入れCache更新で読まれる
 <?php
 return [
     'validate_limits' => [
         'MAX_UPLOAD_SIZE' => 4032,
     ],

 ];
 呼び出しconfig('const.validate_limits.MAX_UPLOAD_SIZE')

■依存性の注入という概念を核とするフレームワーク
依存性の注入,Dependency Injection(DI),Inversion of Control(制御の反転)とも言われ(IoC)
DI コンテナでクラスの生成を1箇所に集約、保守性、柔軟性、テスト性が上がる
composerでカレントディレクトリにPimpleをインストール
 composer require pimple/pimple:~3.0
Illiminate\Foundation\Application クラスが DI コンテナにあたります
<?php
require "vendor/autoload.php";
interface SenderInterface {
    public function send($message);
}
 
class MailSender implements SenderInterface {
    public function send($message) {
        return "メールで送りました。";
    }
}
class GMailSender implements SenderInterface {
    public function send($message) {
        return "GMailで送りました。";
    }
}
class Messenger {
    protected $sender;
    public function __construct(SenderInterface $sender) {
        $this->sender = $sender;
    }
    public function send($message) {
        return $this->sender->send($message);
    }
}
//Config(DIコンテナにバインドすると言う)
$container = new Pimple\Container();
$container['mail'] = function($container) {
    // return new MailSender();
    return new GMailSender();
};
// メールで送る
$messenger = new Messenger($container['mail']);
echo $messenger->send("合格通知") . "\n";
?>

■ジョブキュー(Job Busというのもあるらしい)
時間の掛かる処理を非同期で実行、下記の6種類くらい
sync: デフォルト設定で、実際には同期処理として動きます
database: データベースをジョブキューとして使います
beanstalkd: beanstalkd オープンソースのジョブキューです
sqs: Amazon Simple Queue Service 有料サービスです
データベースの場合
.env、config/queue.phpを編集
ジョブテーブルが必要
php artisan queue:table
php artisan queue:failed-table
php artisan migrate
ジョブの作成
php artisan make:job SendReminderEmail --queued
app/Jobs/SendReminderEmail.php にできるので処理
コンストラクタでこのコマンドに必要なデータを受け取る
handle() メソッドでこのコマンドがやることを実装
routes.phpでQueue::push()やコントローラを作成しdispatch()を使う
データベースの jobs テーブルを確認
select * from jobs;
キューの中のジョブを実行するには、キューリスナーを動かす必要がある
php artisan queue:listen
キューの最初のジョブだけ実行したい場合(リスナーならずっと動くから)
php artisan queue:work

■タスクスケジューラ(Cronの代わり)
app/Console/Commands/Kernel.phpに記載
$schedule->command('auth:clear-reminders')->daily();
$schedule->call('YourClass@someMethod')->hourly();
$schedule->exec('cp oldThing newThing')->weekly()->mondays()->at('13:00');
毎分のcronエントリー1つだけ必要
* * * * * php /path/to/artisan schedule:run 1>> /dev/null 2>&1

■イベントとリスナー
app/Providers/EventServiceProvider.phpに登録
class EventServiceProvider extends ServiceProvider {
    protected $listen = [
        'App\Events\UserWasRegistered' => [ //イベント
            'App\Listeners\SendWelcomeMail', //リスナー
        ],
    ];
}
イベントとリスナーの生成
php artisan event:generate
 event:generateは、a
Comment (0)

■16/1/3 12:00AM
Git

■Git
https://backlog.com/ja/git-tutorial/intro/01/
コマンドを使わずに理解するGit - Qiita
Gitを使いこなしたい!〜仕組みとコマンド24選〜 - Qiita
[初心者向け]GitとGitHubの使い方を徹底解説 - Qiita

Gitのローカルリポジトリには下記がある
リモートブランチ (リポジトリのorigin ブランチのmaster)
=== ↕ ===
ローカルリポジトリ(上記の4つが含まれる)
┣リモート追跡ブランチ (リモートのコピーをローカルに置いている、git fetch、ブランチのorigin/master)
┣ローカルブランチ (git merge/pull、ブランチのmaster)
┣インデックス(コミットデータ git commit)
┗ワーキングツリー (実データ)
ブランチの概要 #初心者 - Qiita
最もポピュラーなリモートリポジトリ名の 「origin」 にあるmaster
ローカルリポにあるリモート追跡ブランチでは「origin/master」
ローカルブランチでは「master」
 origin master はリモートのoriginリポのmasterブランチを○○するという意味
 origin/master はローカルリポにあるリモート追跡ブランチを○○するという意味

Gitクライアント、Sourcetree/TortoiseGit etc
ローカルリポジトリとリモートリポジトリを作りソース管理、通常別途で運用サーバにデプロイ
 ローカルのリモート追跡ブランチ(リモートリポジトリのコピーをfetch)は
 originという名がつきorigin/ブランチ名、リモート/ローカルのデフォブランチは両方通常はmaster
 チェックアウトするとローカルの作業ソースの内容がその版に切り替わる
ワークツリー(作業)とインデックス(中間)を持ちローカル作業をindexにaddしcommit
バグ修正や機能追加などの異なる意味を持つ変更は、できるだけ分けてコミット
コメント 1行目 : 変更内容の要約 2行目 : 空行 3行目以降 : 変更した理由

cloneで、リモートリポジトリの内容を丸々ローカルリポジトリとしてDL
Pullで、リモートリポジトリから最新の変更履歴をローカルリポジトリにDL

Staged changes(index)に入れる → コメントを付記しCommitする
PushしリモートリポジトリにUPする(fast-forwardマージ)

///ブランチ運用モデル https://backlog.com/ja/git-tutorial/stepup/05/
master(デフォ)
hotfix
release
develop
feature
 masterとdvelopの2つがメインブランチで大きな流れ
 releaseブランチでリリースの調整、ブランチ名にrelease-をprefix
 緊急修正の場合hotfixブランチで調整、hotfix-をprefix
 featureブランチで機能追加しdevに纏める
※もしUAT/QAサーバがあるならmaster/developに加えてもう一本QAブランチがあってもいい

[Click for image]



使用(ワークツリー)のブランチを切り替えるにはチェックアウト
HEADとは現在使用しているブランチの先頭を表す(更新の先頭のイメージ)

stashでコミットしていない変更を退避させ、今すぐやりたい作業をしてから、退避させていた変更を取戻して作業を再開することができる(変更があればcommitするまでブランチを切り替えができない)

mergeは複数のブランチを統合し両方の変更を取り込んだマージコミットが作成されます。masterブランチの先頭はそのコミットに移動。non fast-forwardを行うとブランチがそのまま残る

rebaseはmasterブランチの前に別ブランチの内容を付与する。先頭はmasterブランチのままなので、mergeもする。トピックブランチに統合ブランチの最新のコードを取り込む場合はrebaseを使う。iオプションでコミット書換、入替、削除、統合の制御可 git pull --rebase
git pull と git pull –rebase の違いって?図を交えて説明します! – KRAY Inc.
git pull --rebaseをpushする前にやろうという話。 - Qiita
自分の作業中に元のブランチに変更がかかった場合、その変更を取り込んでPushする必要がある>マージかリベース (リベースの方がツリーがきれい)
master ブランチ用のfeat/20240727a の場合
 git pull --rebase origin master
  リモートリポジトリ (origin) から指定されたブランチ (master) の最新の変更をもとにリベース
 git pull-rebase
  カレントの作業ブランチがビハインドなのでPushできないためリベース
 git push
stgプランチ用のfeat/20240727a-stg の場合(stgブランチから切った作業ブランチ、masterから切った作業ブランチをマージしている)
 git pull-rebase origin staging は不要で上記のリベース分をマージする
 git merge feat/20240727a
 git push
ちなみに下記はデフォルトのまま git pull-rebase等のオプションで操作するのが良さそう
 git config pull.rebase false #merge (the default strategy)
 git config pull rebase true #rebase
 git config pull.ff only #fast-forward only
Gitのマージ(pull)方式には、fast-forward & non-fast-forwardの2種類
 ffの早送りは自分の作業ブランチでの走行中に他の修正がなく先頭になる
 デフォルトだと merge なのか fast-forward なのか実行時に判明する

pullを実行してから次のpushをするまでの間に、ほかの人がpushをしてリモートリポジトリを更新してしまっていた場合、マージを行なって他の履歴での変更を取り込むまで自分のpushは拒否されます(競合)マージは自動統合を行うが同じ箇所の場合はエラー。解決をして自分でコミットする必要がある

fetchは単にリモートリポジトリの内容を確認したいだけの時。ローカルに統合したい時はFETCH_HEADをマージするか、改めてpullを実行します

軽量タグと注釈付きタグの2種類。軽量タグはローカルで一時的に使用。タグで過去に戻せる

amend:直前のコミットを修正
revert:指定したコミットを打ち消すコミット
reset:コミットを捨てる
 変更したインデックスの状態を元に戻す(mixed)
 最近のコミットを完全に無かったことにする(hard)
 コミットだけを無かったことにする(soft)
cherry-pick:指定のコミットを現在のブランチに取り込む
mergeのsquashオプション:該当ブランチの全コミットを一つのコミットに纏めて統合

(競合は修正してaddし直したいが、、、)
fetchでエラーがあれば git reset --hard HEAD で作業を取り消し
pullでエラーがあれば git merge --abort と git reset --hard HEAD でマージと作業取り消し
githubでマージエラーがあれば git checkout kuso-sagyo > git pull origin master > 競合解消作業 > git commit > git push origin HEAD

プルリクエスト https://backlog.com/ja/git-tutorial/pull-request/03/
機能追加や改修など作業内容をレビュー・マージ担当者やその他関係者に通知する
 プッシュされたコミットは自動的にプルリクエスト上に反映
 プルリクエストは一覧され未完了のプルリクエストを確認しレビュー・マージ担当者は漏れなく処理可
 ソースコードの変更箇所をわかりやすく表示、コメントのやり取り可
1[開発者] 作業対象のソースを clone または pull
2[開発者] 作業用のブランチを作成
3[開発者] 機能追加、改修といった開発作業
4[開発者] 作業が完了したら push
5[開発者] プルリクエストを作成
6[レビュー・マージ担当者] 通知されたプルリクエストから変更を確認しレビュー
7[レビュー・マージ担当者] レビュー結果を判断し必要ならば開発者にフィードバック
8[レビュー・マージ担当者] レビューの結果、問題がない場合はマージ
9[レビュー・マージ担当者] レビューの結果、対応自体が不要となるなど、プルリクエスト自体が必要ない場合はクローズ
[Click for image]




====================
Gitクライアント Sourcetree
https://eng-entrance.com/sourcetree-use
https://tracpath.com/bootcamp/learning_git_sourcetree.html

新しいverのSourcetreeではインストールができなかった ver 2.6.10.0ならOK
https://www.sourcetreeapp.com/download-archives
 IEは上手く動かず Chromeを使う こちらでもインスコはできなかったが
 SSH鍵をputtyで https://webkaru.net/linux/putty-ssh-login-public-key/
 データフォルダに.gitフォルダがあるので別のGITクライアントでもデータフォルダをDDするだけ
 文字化けはKB4074906では直らなかった、UTF8⇔Shift-jisでも直らん?

Virtualbox等のLinux環境でインストールが良いかも?
sudo apt-get install git だったかと

gitのバージョンを上げる
プロキシを引き継ぎながら最新GITを取れるように ppaをリポジトリに追加
sudo -E add-apt-repository ppa:git-core/ppa
sudo apt update
sudo apt upgrade
git --version
====================
git init --bare --shared 空のリモートリポジトリ(ワークツリーのない開発メンバー全員で使う共有のリポジトリ)を作成
 git remote add origin https://github.com/YY/XX.git リモートリポジトリに名前設定:git remote add [追加リモートリポジトリ名] [追加する場所リポジトリ]
 git remote -v リモートリポジトリの確認

git init 各人使用したいローカルディレクトリに移動しリポジトリ(dir)の.gitを作成
git status 状態確認

git clone https://github.com/YYYYYY/XXXXX.git リモートリポジトリのクローンをローカルに作成
 ※作成した場所にcdし実行、リポジトリのフォルダもできるのでローカルの/home/おまはん/repoで実行でよい
 git clone ssh://user@hostname/path/to/repos GitHub用(鍵登録しssh)

git branch 移動できるブランチとカレントを表示
 git branch -a ブランチの一覧を見る(リモート側?)
git checkout master ローカルの使用ブランチを切替

git pull origin master リモートの該当ブランチをローカルの使用中のブランチに(git pull=fetch+merge)
 git fetch origin master リモートのデータをリモート追跡ブランチに取り込む(変更点を確認
 git merge origin/master リモート追跡ブランチにある変更点を取り込む
 【Git基礎】git push origin masterのmasterはローカル/リモートどっちのこと? #Git - Qiita
  git fetch origin master はリモート側のマスターブランチを差す
  git push origin master はローカル側のマスターブランチを差す
   ソース:行き先 が本来だが行き先が省略されている


git checkout -b hotfix ブランチhotfixへ移動(-bをつける事でカレントから新規にブランチを作成し移動)
 git branch hotfix ブランチhotfixを作成(カレントから?
 git checkout hotfix ローカルの使用ブランチを切り替え
 ※hotfixで修正する場合

git checkout a.txt 特定ファイルの変更を取り消す
git checkout . add前の未追跡編集ファイルの取り消し、作業を取消し別ブランチ切替ができるように
git clean -df . add前の未追跡新規ファイルの取り消し
git reset HEAD . addを取り消し
gitでいろいろ取り消したい #Git - Qiita

git add a.txt 削除したファイルをaddでリポジトリから削除
git rm a.txt リポジトリから削除しディレクトリからも削除
git rm --cached a.txt リポジトリから削除するがディレクトリには残す
 rm a.txt ディレクトリから削除、リポジトリには残す

git add -n . アドの対象を表示
 git add . カレントdir以下の全てをIndex追加(危ない)
 git add *.py ワイルドカード
 git add a.txt ファイル追加
 git add dirA ディレクトリ追加
 git add -A 変更のあったすべてgit add --all

git reset . addを取消す

git diff --cached ステージング(index追加)の内容確認

git commit ローカルレポにindexをコミット、エディターが開く、iインサートモードで編集、ESC:wqで保存
 git commit -a -m "comment" -aは変更点がある登録された全ファイル

git log コマンドでコミットの履歴
git show コミットした内容自体を確認
git reset --soft HEAD^ コミットの取り消し

git cherry-pick _commit_id_ 他ブランチでコミットした内容をカレントブランチに適応

 ※コミットが済んでいないファイルが残っている状態で git checkout の別ブランチ作成や git merge でmasterにマージしない事
 git reset --hard HEAD^ 直前のコミットを取消(hardはcommit logが残らず、indexやworktreeもオプで戻せる、綺麗だが競合?)
 git reset --hard HEAD~2 数字で複数コミット取消
 git stash 退避/git stash list 退避リスト

git revert コミットNo. 直前のコミットを取消す再コミットでチーム開発向き(戻したcommit logが残る)
 →commit文を書かされるので保存してgit push
 誤ってmasterで修正をしてpushしてしてしまったときにはこれ
  git logでコミットIDをしらべ、git revert <commitID>、git pushでmasterにpushしてしまう

git branch -vv 上流ブランチの名前を確認
git config --get remote.origin.url リモートのリポジトリURLの確認

 git pull --rebase origin master push前にリモートのmasterの更新を取り込んでおけばコミットが綺麗になる

git push リモートに同ブランチがあれば通るがなければ下の--set-upstreamでリモートに作る
 git push --set-upstream origin hotfix push時にリモートに該当ブランチを作成

git push リポジトリ名 ローカルブランチ名:リモートブランチ名 が本来
 リモートリポジトリのアクセス先がoriginという名前で省略設定されている
  git push origin 20250101aaa:20250101aaa -> git push
 これ以外の場合は通常github等のリポジトリ側でPR先を変えるといいと思われるが、、
git pushの取り消しはgithub上でブランチを削除すればよいのではと

リモートでPull Requestを作成
 レビュアーはPull Reqeustを確認し問題なければリモートにマージする
 hotfix等はPull Requestをmasterにするが同時にその1度のpushをdevelopにもPull Requestする/マージも
 プルリクエストでコンフリクトしたときの対処方法(備忘録) - Laravel学習帳 (laraweb.net)

サーバーにデプロイ
 git checkout master マスターブランチに移動
 git pull マスターからデータを取る
 デプロイ作業(サーバにアップロード/terraform apply)

戻し(リバートバック)用にPRを事前準備をしたい
 空のaddやcommitはできない
 githubのコミットグラフで不要なコミット上でRevertボタンを押すとPRが作成される
 戻す可能性がある場合はmasterにマージせず作業ブランチでApply(下記リモートからデータ取得参照)
  ダメならmasterでApplyし直す、あるいはRevertボタンを押す

リモートからデータを取ってくる
git branch -r リモート追跡ブランチの一覧(pullしとけば出る)
git fetch origin darekanounko リモートにあるブランチを取得
git checkout darekanounko これで切り替えができる
 なければ同名で作ってpull? git checkout -b darekanounko > git pull これはダメ

git initの取消し
 rm -rf .git でgit initした場所の.gitファイルを削除
gitの作り直し(最悪これで回復)
 rm -rf .git でgit initした場所の.gitファイルを削除し、改めて git clone

git branch -d hotfix/unco072 hotfixブランチを削除 -dはマージ済みのもの、-Dで何でも削除
git push origin :hotfix/unco072 hotfixブランチをリモートから削除

git branch --merged マージしたブランチ一覧
git branch --merged | egrep -v "\*|master|develop" マージしたブランチmaster/dev以外
git branch --merged | egrep -v "\*|master|develop" | xargs git branch -D それらを削除
git remote prune origin リモートで消されたブランチをローカル反映 prune余分な枝を落とす
git branch --format "%(refname:short) %(upstream:track)" ブランチ名とトラック状態を取得
git branch --format "%(refname:short) %(upstream:track)" | grep "\[gone\]" 削除された[gone]のみ取得
git branch --format "%(refname:short) %(upstream:track)" | grep "\[gone\]" | awk '{print $1}' | xargs -IXXX git branch -D XXX それらはリモートで削除されており削除
git stash clear スタッシュを消す
git gc ガーベージコレクション
 よく使う お掃除系 git 便利コマンド集 (foresta.me)

コンフリクト
コンフリクトはgithubのPRのマージボタンがあるあたりで見れる
masterから修正ブランチを切り出しているのでstgブランチにマージしようとするとコンフリクトする
(masterには未だマージされていないが、stgには既にマージされているPR)
例えば
<<<< feat/kuso
kuso2 = dead
====
kuso = smells
>>>> stg
github上のresolve conflictで更新を掛けるとfeat/kusoPRに反映され、DEV/STGだけでなく本番へも変更が掛かってしまうため、DEV/STGから切りなおしfeatを合体させコンフリクト解消する
git checkout master
git pull
git switch stg
git pull
git switch -c feat/kuso-stg
git merge feat/kuso
コンフリクトを手修正
git add/commit/push (addがなくても強引にpushすればよい?)
githubでstgへ向けてPRを作成
元のstgへのfeat/kusoPRは却下する

git pull で下記エラー
error: cannot lock ref 'refs/tags/v1.3.38': unable to resolve reference 'refs/tags/v1.3.38': reference broken
→cat .git/refs/tags/v1.3.38 .git内のことで対象を見る、GUIフォルダでも大体空
→rm .git/refs/tags/v1.3.38
→RMすると大概git pullができるようになっている
reference brokenでgit pullできなくなったときの対策 - Qiita

他の対応方法として
→rm -rf おまはんのリポジトリのフォルダ フォルダごと消す
→git clone https://やり直しなはれ クローンやり直す
→git checkout master > git pull > git checkout 飛んだbranch (事前にgit pushで保存しとく)

not something we can merge のエラー
ブランチ名のスペル入力間違い、あるいはローカルにないブランチでmaster pull>該当ブランチにcheckoutしpull等が必要

.gitignoreという設定ファイルにデフォルトで対象外とするファイルを設定
.git/config を見るとどのURLにpushするか分かる
リポジトリが不要になったときは.git dirを削除する。管理ファイル(ワークツリーにないファイル)が削除されワークツリーのファイルはそのまま影響がない
 ローカルリポジトリで複数のブランチがある場合、必要なブランチをチェックアウト後ワークツリーのファイルを別途保存してから削除することを勧める
リポジトリを作成し直したい場合は.git dirを削除してから改めてgit initを実行

https://www.atmarkit.co.jp/ait/articles/2003/12/news010.html init
https://www.atmarkit.co.jp/ait/articles/2003/05/news006.html clone
https://www.atmarkit.co.jp/ait/articles/2003/06/news019.html pull
https://www.atmarkit.co.jp/ait/articles/2003/13/news031.html add
https://www.atmarkit.co.jp/ait/articles/2003/19/news018.html commit
 git: git stashの使い方 - Qiita

https://www.atmarkit.co.jp/ait/articles/2005/21/news023.html push
https://www.atmarkit.co.jp/ait/articles/2005/22/news028.html push衝突

https://www.atmarkit.co.jp/ait/articles/2004/24/news028.html checkout
https://www.atmarkit.co.jp/ait/articles/2004/30/news017.html checkout2

https://www.atmarkit.co.jp/ait/articles/2006/11/news025.html revert
https://www.atmarkit.co.jp/ait/articles/2007/01/news015.html revert2
https://www.atmarkit.co.jp/ait/articles/2007/10/news010.html revert3
https://www.atmarkit.co.jp/ait/articles/2007/27/news013.html reset
 Git revertとresetについて - Qiita

https://www.atmarkit.co.jp/ait/articles/2004/09/news018.html log
https://www.atmarkit.co.jp/ait/articles/2004/16/news025.html log2
https://www.atmarkit.co.jp/ait/articles/2004/17/news021.html show
https://www.atmarkit.co.jp/ait/articles/2004/23/news034.html show2
https://www.atmarkit.co.jp/ait/articles/2005/01/news024.html tag
https://www.atmarkit.co.jp/ait/articles/2005/07/news009.html tag2

https://www.atmarkit.co.jp/ait/articles/2005/08/news017.html remote
https://www.atmarkit.co.jp/ait/articles/2005/14/news025.html remote2

https://www.atmarkit.co.jp/ait/articles/2006/04/news022.html rm
https://www.atmarkit.co.jp/ait/articles/2006/05/news021.html rm2
https://www.atmarkit.co.jp/ait/articles/2006/12/news017.html mv

■ツール
git :: bash プロンプトにブランチ名と作業ツリーの状態を表示する [Tipsというかメモ] (root-n.com)
home/.bashrcに追記、1行目が切れがちで注意


Comment (0)

■16/1/2 12:00AM
Agile
■アジャイル
http://codezine.jp/article/detail/5465
10年ちょっとやってきた僕のアジャイル開発の現在地
スコープを柔軟化するため、スプリントで区切り、スプリント後は動作するようにする、しかしスケジュール/リソースは固定
ボトムアップとして(ストーリー、タスクなどの)バックログ。WBSはトップダウンだった
スクラムやXP等を使う
- 要求機能はストーリーとして表現される(エピックはストーリーを一定のジャンルでグループ化し可視化)
- プロダクトバックログを作成し、直近のリリース(スプリント数個分)ごとにリリースバックログを作成する
- スプリントごとにスプリントバックログとして作業を詳細化(時間単位)する
- スプリントの完了時には、テストされ、デモが可能な状態でなければならない
スプリントを導入しても、設計、実装、単体テスト、結合テストとタスクを分割していたのでは、ミニウォーターフォール
 逆に管理工数がかかったり品質が下がるかも。継続的統合やテスト駆動開発などが要る

1.計画を簡易的に行なう(スプリント単位で等)
2.その代わり定期的に見直す
3.簡易な計画/管理ツールを利用する
4.チームとしてスケジュールを組む

粗い要求までもリストアップするプロダクトバックログ(エクセルレベルで可)
次のリリースのためのリリースバックログ(エクセルレベルで可)
直近のスプリントのためのスプリントバックログ(タスク管理システムにすると意識しなくても良いかも)

初日にプランニングミーティングを行い、直近(2週間等)のタスクを計画
そして、毎日デイリースクラムを実施し、最終日にレトロスペクションミーティングで締めて
スプリントの1サイクルとなる

ミーティングでは、タスク管理システムないしは、
ホワイトボードに張られたタスクやバックログリスト(場合によってはガントチャート)などの
タスクを一覧できるツールを用いる

 ///プランニングミーティング
 プロダクトバックログから優先順位に従って選択したストーリーをタスクにブレークダウン
 ミーティング前
  1.タスクを決定する
 ミーティング時
  2.前スプリントのタスク完了を確認する
  3.メンバーの予定作業時間を確認する
  4.スプリントのゴールを共有する
  5.タスクを説明し工数を見積もる
 ミーティング後
  6.タスクを割り当てる

 ///デイリースクラム
昨日何をしたか?
今日何をするか?
進捗を妨げている問題は何か?

 ///レトロスペクションミーティング
 何をしたかについて、1人数分から十数分説明する
 何ができないか、どのような課題が残っているかに言及する


↓↓↓↓↓↓アジャイルから明確にスクラム

■スクラム
https://qiita.com/gold-kou/items/90ba982a14ca79d843c9
///対話の価値(左が要らないとは言っていない)
プロセスやツール < 個人と対話
包括的なドキュメント < 動くソフトウェア
契約交渉 < 顧客との対話
計画に従う < 変化への対応

透明性:スクラムチームの現状や問題点を見える化すること
検査:見える化により問題点を見つけること
適応:スクラムチームに問題があった場合に、改善策を考えて対処すること

個人はスクラムチームのゴールの達成を確約しなければならない
メンバーは正しいことをする勇気を持ち困難な問題に取り組まなければいけない
全員がスプリントの作業とスクラムチームのゴールに集中しなければいけない
スクラムチームとステークホルダーは仕事や課題とその遂行の様子を公開する
メンバーはお互いを能力のある独立した個人として尊敬しなければいけない

 ///プロダクトオーナー(PO)
- プロダクトバックログアイテム(PBI)の管理
- リリース計画策定
- 市場調査
- ステークホルダとの対話
- 開発チームとの対話
- 予算管理
- スプリントレビューで"DONEの定義"にしたがって受け入れ判断をする
注意点:POを複数人で実施することも可能だが、その場合は代表者を1人決めておく必要がある。

 ///スクラムマスター(SM)
- 妨害リストの管理
- 妨害の除去
- POのサポート
- 開発チームのサポート
注意点:チームが自己組織化するために、SMはメンバーに直接的な指示(タスク割り当てなど)や管理(進捗管理など)をしてはいけない。スクラムにはPMは存在しない。

 ///開発チーム
- 開発、インクリメント(成果物)を完成
- プロダクトバックログの追加
- 妨害リストの追加
注意点:開発チームは自己組織化されており、外部から仕事の進め方やタスクを指示されるようなことがあってはならない。1つの開発チームは3-9人で構成されなければならない。それより少ないと機能横断的でなくなる可能性が高くなるためNG。多すぎると調整が大変になってしまうためNG。多すぎる場合は別のスクラムチームをもう1つ作るべき。開発チーム内で年齢や役職、会社の違いがあったとしても全員フラットな関係でなければならない。開発チームに専門能力の高いメンバーがいて、その分野ではその人に頼りがちだけど、そこで問題が起きたとしてもそれはそのメンバーだけの責任でなく開発メンバー全員の責任であることに注意する。

 ///ユーザーストーリー形式 
「〜として、〜したい。それは、〜だからだ。」を書きます。これは、INVEST(Independent, Negotiable, Valuable, Estimable, Small, Testableの頭文字をとったもの)を満たす必要があります。プロダクトバックログのDoneの定義も定義

 ///スプリントバックログ
開発チームにより作成される。スプリント期間中での修正、追加、削除も可能である。スプリントバックログの管理を行えるのは開発チームだけである。規模や工数はバラバラで問題ないが、1つのスプリントバックログアイテムの見積もりは1日以下の大きさに分割すること。スプリントバックログアイテムは"未着手"、"着手中"、"完了"などのいずれかのステータスがラベル付けされており、そのステータスが可視化されている必要がある。JIRAでも付箋とホワイトボードによるアナログ方式でもどちらでも問題ない。自己組織化されているので、誰かに割り当てられるものではなく自ら挙手する。設計、コーディング、テストコードの作成だけでなく、結合テストなどの実施、CI環境改良、ノウハウを残すなどもスプリントバックログとして分割するべきである。

 ///妨害リスト
スクラムチーム内外で、妨害になっていることを優先度順にリスト化したものである。どのロールの人でも追加できるが、妨害リストの管理の責任者はSMである。いつでも追加可能だが、特にデイリースクラムやスプリントレトロスペクティブで課題(妨害)の共有が行われやすい。

 ///スプリント
1ヶ月以内の固定した開発期間を何度も繰り返す。1週間、2週間、1ヶ月のいずれかが一般的。スプリント期間内に完了しなかったプロダクトバックログアイテムがあったとしても、スプリントを延長してはいけない。スプリントの中止はよっぽどのことが無い限り、行われるべきでないが、もしものときは実行権限を持つのはPOだけである。

 ///スプリント0
自己紹介、スクラム理論の勉強会(認識合わせ必須)、当面のプロダクトバックログの作成と共有、必要技術スキルの勉強会、開発環境構築などを行うスプリント開始前の準備期間のこと。必要期間はチームにより異なる。

 ///リリーススプリント
インクリメントの統合テストや通常スプリントで残ったタスクを実施してリリースを行うための特別なスプリント。 原則、このスプリントは存在しません。スプリント毎にリリース可能なものを作成するので、リリーススプリントが無いことが理想だが、そうでない場合に用意される。リリーススプリントはPOがリリース準備が整ったと判断するまで続く。

 ///イベント
開発チームでイベント以外のミーティングができるだけ行われないようにする。全てのイベントはタイムボックス化されている(何分とか、何時間までとかが決まっている)。もし、タイムボックスを超えてしまった場合、議論をそこで打ち切る必要はないが、タイムボックスを越してしまったという事実は妨害リストに追加したり、スプリントレトロスペクティブで話し合う必要がある。

 ///スプリントプランニング
第一部は全てのロールが参加する。ただし、第二部はPOは連絡がつく状態であれば参加しなくてもよい。第一部では、ベロシティをもとに今回のスプリントで対応できそうな範囲でプロダクトバックログを開発チームが選択する。(POが選択してはいけない)
プロダクトバックログはリファインメントによってすでに見積もりがされている状態のはずだが、リファインメント後に新規追加されたプロダクトバックログアイテム(つまり見積もりがまだされていないもの)が含まれている場合は、この場でリファインメントを実施する。第二部では、開発チームは選択したプロダクトバックログアイテムをスプリントバックログに分割する。これにより、POとSMにどのようにプロダクトバックログアイテムを消化できるかを伝えられる状態になる。分割したスプリントバックログに時間数を見積もり、その総計が1スプリントでのチームが開発できる時間をオーバーした場合は優先度の低いプロダクトバックログから外していく。逆に余裕がある場合なプロダクトバックログを追加する。POの予想より多すぎたり少なすぎたりする場合は、開発チームとPOは話し合う。開発チームは選択したプロダクトバックログアイテムを完了することに全力を注がなければならないが、完了することを約束するわけでない。なぜならば、見積もり時にはわからなかった技術的に困難な事象が発生することはよくあるからである。無謀な長期の残業は行わない。

 ///デイリースクラム
- いつ?⇒毎日同じ時間(一般的には朝)
- どこで?⇒どこでもいいがいつも同じ場所
- 誰が?⇒開発メンバー。SMは必要に応じて参加する。POは見学(発言は基本的にNG)してもよい。
- 目的は?⇒開発チームの検査
- どれくらい?⇒最大15分
昨日やったこと、今日やること、あれば課題(妨害)の共有。課題は共有に留めること。管理職の見学は避けたい。なぜならば、共有でなく管理職への報告になりがちで自己組織化が損なわれやすいのと、課題共有がしにくい雰囲気になりやすいからである。もし管理職にエスカレーションするべき課題があるならば、デイリースクラム後に報告すること。

 ///プロダクトバックログリファインメント
- いつ?⇒スプリント期間中であればいつでも良い。
- 誰が?⇒全てのロール
- 目的は?⇒プロダクトバックログの透明性の獲得
- どれくらい?⇒最大1日(8h)
POはプロダクトバックログの説明(前回からの変更分、追加分、削除分、詳細決定部分、順序変更部分など)を行う。リファインメントの対象は、次のスプリントあるいはさらにその次のスプリントで開発対象となりそうなくらいの範囲となる。優先度低いプロダクトバックログの説明までは行う必要はない。本当に実施するかも不透明だし変更も発生しやすいため。開発チームはプロダクトバックログへの意見(追加アイディアや順序変更や質問など)、新しいプロダクトバックログアイテムの見積もり、必要であれば再見積もりを行う。

 ///プランニングポーカー
プロダクトバックログの見積もりには"プランニングポーカー"と呼ばれる手法が一般的に使われる。プ開発チーム全員がフィボナッチ数列(0, 0.5, 1, 2, 3, 5, 8, 13,...∞)の値が書かれたカードを持ち、プロダクトバックログアイテムごとに「いっせーのせ!」でカード(見積もり)を提示する。この見積もりは単位のない"相対見積もり"である。見積もりの値が似たような値で均等に分散したとき(例えば5が3人で8が3人の場合)は、悲観的に考えて大きい値(ここでは8)をストーリポイントとして計算したほうがよい。

 ///スプリントレビュー
- いつ?⇒スプリントの終わり
- 誰が?⇒全てのロールとPOが招待した重要な関係者(ステークホルダーなど)
- 目的は?⇒インクリメントの検査とプロダクトバックログの適応
- どれくらい?⇒最大2時間(スプリント2週間の場合)
開発チームはインクリメントのデモを実施して、質問に答える。また、完了できなかったプロダクトバックログがあれば説明する。開発チームはデモをやることと受け入れに通ることばかり考えがちだが、大切なのはスプリントレビューを通して、よりよいプロダクトを得るための手がかりを見つけることである。デモはそのための手段であって目的ではないことに注意しよう。また、デモの説明スライドは作るべきでない。そこに労力が割かれがちだからだ。POはDONEの定義に従い、インクリメントが受け入れ可能かを判断する。

 ///スプリントレトロスペクティブ
- いつ?⇒スプリントレビュー後かつ次のスプリントが開始される前
- 誰が?⇒SMと開発チーム。POは参加してもよいが必須でない。
- 目的は?⇒スクラムチームの検査と適応
- どれくらい?⇒最大1.5時間(1スプリント2週間の場合)
スプリントで人・プロセス・ツールの観点から問題がなかったか、もっと成果を出すためにできることがないかを議論し、次回のスプリント以降の改善策を考える。手法として、KPT(Keep, Problem, Tryを出し合うこと)がよく使われる。

 ///ベロシティ
ベロシティとは、そのスプリントで開発したプロダクトバックログのストーリーポイントの合計である。スクラムチームはベロシティを高めて安定させることを目指す。そもそもストーリーポイントがいい加減な値。フィボナッチ数列の値しかだせないし、開発者は技術的な不安点があるからとりあえず大きな値をしばしば出しがちである。さらに、スプリントの日数は祝日、メンバの休暇などでスプリント毎に異なるものである。

スクラムではテスト専門チームの存在を許していない。スプリントの中で徹底的にバグを発見して潰すべきなのである。

■スクラムガイド Ken Schwaber & Jeff Sutherland
2020-Scrum-Guide-Japanese.pdf (scrumguides.org)

■マネジメント向け アジャイルの利点
https://www.ryuzee.com/contents/blog/13147

■カンバン
ToDo、進捗、ワークフロー整理、振り返り、で活用できれば
カンバンの基本のキ - Qiita
※なおトヨタのカンバン方式は意味自体はJust in timeの意、中で使われていた伝達法もTodoリストと違う、生産と運搬のタグみたいなもの

■JIRA
コンポーネントはプロジェクトを分割(トップダウン)
エピックはストーリーを一定のジャンルでグループ化(ボトムアップ)
タスクはスプリントの期間で終わり作業が内容が分かるタイトルに
ラベルがあれば時間集計できるので作業ラベル


Comment (0)

Navi: <  18 | 19 | 20 | 21  >
-Home
-Column [133]
-Europe [9]
-Gadget [77]
-Web [137]
-Bike [4]

@/// BANGBOO BLOG ///