Slide 1

Slide 1 text

༧๷ʹউΔ๷ޚͳ͠ ʢ೥൛ʣ ࿨ా୎ਓ rev.24 /PW !1)1ΧϯϑΝϨϯε෱Ԭ !U@XBEB !UXBEB 📷🙆 🙆 !UXBEB JEUXBEB #phpconfuk ݎ࿚ͳίʔυΛಋ༷͘ʑͳઃܭͷώϯτ #hall_hz

Slide 2

Slide 2 text

class BugRepository { private $pdo; public function __construct($pdo) { $this->pdo = $pdo; } public function findAll($params) { $sql = 'SELECT bug_id, summary, reported_at FROM Bugs WHERE reported_at >= :startAt AND reported_at < :endAt AND status = :status'; $stmt = $this->pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(\PDO::FETCH_CLASS, Bug::class); } ஫ॻ੶ʰ42-Ξϯνύλʔϯʢॳ൛ʣʱͷͻͲ͍ίʔυྫΛ͞ΒʹͻͲ͘ΞϨϯδͯ͠ॻ͍͍ͯ·͢ ͱ͋Δͱ͜Ζʹɺ͜Μͳίʔυ͕͋Γ·ͨ͠ # " % IUUQTXXXPSFJMMZDPKQCPPLT

Slide 3

Slide 3 text

public function findAll($params) { $sql = 'SELECT bug_id, summary, reported_at FROM Bugs WHERE reported_at >= :startAt AND reported_at < :endAt AND status = :status'; $stmt = $this->pdo->prepare($sql); $stmt->execute($params); return $stmt->fetchAll(\PDO::FETCH_CLASS, Bug::class); } ॲཧࣦഊͷݪҼʹͳΔՄೳੑ͕͋Δͷ͸Ͳͷߦʁ # " % ᶃ ᶄ ᶅ ᶃςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ ᶄQBSBNT͕OVMM ᶄQBSBNTͷΩʔ໊΍਺ͷෆҰக ᶄQBSBNTͷ஋͕จࣈྻʹม׵ෆೳ ᶄQBSBNTͷ஋͕೔࣌ͱͯ͠ղऍͰ͖ͳ͍ ᶅ#VHΫϥε͕ະఆٛ ᶃᶄᶅ్தͰσʔλϕʔε઀ଓΤϥʔ ຊ೔ͷߨԋ͸ ͜ΕΒͷ໰୊ʹूத͠·͢ 👉 👉 👉 👉

Slide 4

Slide 4 text

ෆ۩߹ͷൃݟ͕ ஗ΕΕ͹஗ΕΔ΄Ͳ ই͸ਂ͘ͳΔ IUUQTXXXBTURCPSHQSFTTSPPN*452#@$FSUJ fi DBUJPO@/FXT@@IUNM

Slide 5

Slide 5 text

ݎ࿚ͳίʔυ΁ͷޡղ

Slide 6

Slide 6 text

public function findAll($params) { if (is_null($params)) { throw new \InvalidArgumentException('params should not be null'); } if (!is_array($params)) { throw new \InvalidArgumentException('params should be an array'); } if (count($params) !== 3) { throw new \InvalidArgumentException('params should have exact three items'); } if (!array_key_exists('startAt', $params) || !array_key_exists('endAt', $params) || !array_key_exists('status', $params)) { throw new \InvalidArgumentException('params should have keys "startAt", "endAt" and "status"'); } if (!is_string($params['startAt'])) { throw new \InvalidArgumentException('params["startAt"] should be a string'); } if (!is_string($params['endAt'])) { throw new \InvalidArgumentException('params["endAt"] should be a string'); } if (!is_string($params['status'])) { throw new \InvalidArgumentException('params["status"] should be a string'); } if (!in_array($params['status'], ['OPEN', 'NEW', 'FIXED'], true)) { throw new \InvalidArgumentException('params["status"] should be in "OPEN","NEW","FIXED"'); } ͨͩͻͨ͢ΒೖྗΛ๷ޚతνΣοΫͨ͠Γ # " %

Slide 7

Slide 7 text

ෆਖ਼ͳೖྗ͕͋ͬͯ΋ࣗ෼ͰԿͱ͔͠Α͏ͱͨ͠Γ # " % $sql = 'SELECT bug_id, summary, reported_at FROM Bugs WHERE reported_at >= :startAt AND reported_at < :endAt AND status = :status'; $stmt = $this->pdo->prepare($sql); $safeParams = [ 'startAt' => $params['startAt'] ?? $params['start_at'] ?? '1970-01-01', 'endAt' => $params['endAt'] ?? $params['end_at'] ?? 'now', 'status' => $params['status'] ?? 'OPEN', ]; $stmt->execute($safeParams); $className = class_exists('Bug') ? 'Bug' : 'BugModel'; return $stmt->fetchAll(\PDO::FETCH_CLASS, $className);

Slide 8

Slide 8 text

υΩϡϝϯτͰؒҧ͍΍͢͞Λิ͓͏ͱͨ͠Γ # " % /** * 指定した範囲の日時およびステータスに合致する Bug を検索し、ヒットした全件を Bug オブジェクトの配列として返す。 * * @param array $params 格納した検索条件の連想配列。キー "startAt" に検索範囲の 始点日時をstringで, キー "endAt" に検索範囲の終点日時をstringで, キー "status" にス テータス文字列をstringで指定すること。キー、値それぞれNULLは不可とする。 * @return Bug[] 検索結果を Bug オブジェクトにマッピングして返す。検索結果が0件 のときは空配列を返す。 */ public function findAll($params) {

Slide 9

Slide 9 text

ݎ࿚ͳίʔυͱ͸ ѱ͍ίʔυʹឺ૑ߣΛ ͋ͯͨ΋ͷͰ͸ͳ͍

Slide 10

Slide 10 text

lݡ໌ͳιϑτ΢ΣΞٕज़ऀʹͳΔͨΊͷ ୈҰา͸ɺಈ͘ϓϩάϥϜΛॻ͘͜ͱͱ ਖ਼͍͠ϓϩάϥϜΛద੾ʹ࡞੒͢Δ͜ͱ ͷҧ͍Λೝࣝ͢Δ͜ͱz Š."+BDLTPO 

Slide 11

Slide 11 text

Agenda  ܕએݴ  ྻڍܕ  ੩తղੳ  ܕͷϞσϦϯά  ෆมੑ  ׬શੑ  ੹຿ͷ഑ஔ 👉

Slide 12

Slide 12 text

औΓಘΔܕΛݮΒ͢

Slide 13

Slide 13 text

ΤοηΠ+ݟ஌Β͵ਓͱ΋͏·͘΍Δʹ͸ lʮग़དྷͯ͸ͳΒ͵͜ͱΛې͡ ΔʯͷͰ͸ͳ͘ɺ͸͡Ί͔Β ʮग़དྷ͍͍ͯ͜ͱ͚ͩΛग़དྷΔ Α͏ʹ͢Δʯͱߟ͑ΔͷͰ͢z IUUQTXXXPSFJMMZDPKQCPPLT

Slide 14

Slide 14 text

public function findAll($params) { if (is_null($params)) { throw new \InvalidArgumentException('params should not be null'); } if (!is_array($params)) { throw new \InvalidArgumentException('params should be an array'); } if (count($params) !== 3) { throw new \InvalidArgumentException('params should have exact three items'); } if (!array_key_exists('startAt', $params) || !array_key_exists('endAt', $params) || !array_key_exists('status', $params)) { throw new \InvalidArgumentException('params should have keys "startAt", "endAt" and "status"'); } if (!is_string($params['startAt'])) { throw new \InvalidArgumentException('params["startAt"] should be a string'); } if (!is_string($params['endAt'])) { throw new \InvalidArgumentException('params["endAt"] should be a string'); } if (!is_string($params['status'])) { throw new \InvalidArgumentException('params["status"] should be a string'); } if (!in_array($params['status'], ['OPEN', 'NEW', 'FIXED'], true)) { throw new \InvalidArgumentException('params["status"] should be in "OPEN","NEW","FIXED"'); } ໰୊ͱͳ͍ͬͯΔ͜ͷ࿈૝഑ྻΛ ݱঢ়

Slide 15

Slide 15 text

public function findAll(\DateTime $startAt, \DateTime $endAt, string $status): array { if (is_null($params)) { throw new \InvalidArgumentException('params should not be null'); } if (!is_array($params)) { throw new \InvalidArgumentException('params should be an array'); } if (count($params) !== 3) { throw new \InvalidArgumentException('params should have exact three items'); } if (!array_key_exists('startAt', $params) || !array_key_exists('endAt', $params) || !array_key_exists('status', $params)) { throw new \InvalidArgumentException('params should have keys "startAt", "endAt" and "status"'); } if (!is_string($params['startAt'])) { throw new \InvalidArgumentException('params["startAt"] should be a string'); } if (!is_string($params['endAt'])) { throw new \InvalidArgumentException('params["endAt"] should be a string'); } if (!is_string($params['status'])) { throw new \InvalidArgumentException('params["status"] should be a string'); } if (!in_array($params['status'], ['OPEN', 'NEW', 'FIXED'], true)) { throw new \InvalidArgumentException('params["status"] should be in "OPEN","NEW","FIXED"'); } ͦΕͧΕܕએݴ͞ΕͨҾ਺ʹ෼ղ͢Δ ܕએݴʹΑͬͯʮग़དྷ͍͍ͯ͜ͱ͚ͩΛग़དྷΔʯΑ͏ʹ

Slide 16

Slide 16 text

public function findAll(\DateTime $startAt, \DateTime $endAt, string $status): array { if (is_null($params)) { throw new InvalidArgumentException('params should not be null'); } if (!is_array($params)) { throw new InvalidArgumentException('params should be an array'); } if (count($params) !== 3) { throw new InvalidArgumentException('params should be have exact three items'); } if (!array_key_exists('startAt', $params) || !array_key_exists('endAt', $params) || !array_key_exists('status', $params)) { throw new InvalidArgumentException('params should have key "startAt", "endAt" and "status" only'); } if (!is_string($params['startAt'])) { throw new InvalidArgumentException('params["startAt"] should be a string'); } if (!is_string($params['endAt'])) { throw new InvalidArgumentException('params["endAt"] should be a string'); } if (!is_string($params['status'])) { throw new InvalidArgumentException('params["status"] should be a string'); } if (!in_array($params['status'], ['OPEN', 'NEW', 'FIXED'], true)) { throw new \InvalidArgumentException('params["status"] should be in "OPEN","NEW","FIXED"'); } ๷ޚతνΣοΫ͕΄΅ෆཁʹ

Slide 17

Slide 17 text

૝ఆ͠ͳ͚Ε͹ͳΒͳ͍ঢ়گ͕ݮͬͨ const TIMESTAMP_FORMAT = 'Y-m-d\TH:i:s.uP'; public function findAll(\DateTime $startAt, \DateTime $endAt, string $status): array { if (!in_array($status, ['OPEN', 'NEW', 'FIXED'], true)) { throw new \InvalidArgumentException('status should be in "OPEN","NEW","FIXED"'); } $sql = 'SELECT bug_id, summary, reported_at FROM Bugs WHERE reported_at >= :startAt AND reported_at < :endAt AND status = :status'; $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':startAt', $startAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':endAt', $endAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':status', $status, \PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(\PDO::FETCH_CLASS, Bug::class); } ˞ຊߨԋͰ͸1PTUHSF42-ͷUJNFTUBNQXJUIUJNF[POFܕͷΧϥϜʹ֨ೲ͍ͯ͠Δͱߟ͍͑ͯͩ͘͞

Slide 18

Slide 18 text

Agenda  ܕએݴ  ྻڍܕ  ੩తղੳ  ܕͷϞσϦϯά  ෆมੑ  ׬શੑ  ੹຿ͷ഑ஔ 👉

Slide 19

Slide 19 text

औΓಘΔ஋ΛݮΒ͢

Slide 20

Slide 20 text

wݴޠ૊ΈࠐΈͷܕʢJOU TUSJOH౳ʣΛ࢖͏ ͱɺऔΓಘΔ஋ͷ૊Έ߹Θ͕ͤ๲େʹͳΔ w໰୊ྖҬͷ஌ࣝΛ׆༻ͯ͠ݻ༗ͷܕΛ࡞Δ ͜ͱͰɺऔΓಘΔ૊Έ߹ΘͤΛେ෯ʹݮΒ ͤΔ ΤοηΠؔ਺ͷʮαΠζʯΛখ͘͢͞Δ IUUQTXXXPSFJMMZDPKQCPPLT

Slide 21

Slide 21 text

ྻڍܕΛ࢖͓͏ 1 ) 1   enum Status: string { case Open = 'OPEN'; case New = 'NEW'; case Fixed = 'FIXED'; } IUUQTXXXQIQOFUNBOVBMKBMBOHVBHFFOVNFSBUJPOTQIQ

Slide 22

Slide 22 text

const TIMESTAMP_FORMAT = 'Y-m-d\TH:i:s.uP'; public function findAll(\DateTime $startAt, \DateTime $endAt, string $status): array { if (!in_array($status, ['OPEN', 'NEW', 'FIXED'], true)) { throw new \InvalidArgumentException('status should be in "OPEN","NEW","FIXED"'); } $sql = 'SELECT bug_id, summary, reported_at FROM Bugs WHERE reported_at >= :startAt AND reported_at < :endAt AND status = :status'; $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':startAt', $startAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':endAt', $endAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':status', $status, \PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(\PDO::FETCH_CLASS, Bug::class); } ݱঢ়

Slide 23

Slide 23 text

const TIMESTAMP_FORMAT = 'Y-m-d\TH:i:s.uP'; public function findAll(\DateTime $startAt, \DateTime $endAt, Status $status): array { if (!in_array($status, ['OPEN', 'NEW', 'FIXED'], true)) { throw new \InvalidArgumentException('status should be in "OPEN","NEW","FIXED"'); } $sql = 'SELECT bug_id, summary, reported_at FROM Bugs WHERE reported_at >= :startAt AND reported_at < :endAt AND status = :status'; $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':startAt', $startAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':endAt', $endAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':status', $status->value, \PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(\PDO::FETCH_CLASS, Bug::class); } Ҿ਺Λจࣈྻ͔Βྻڍܕʹม͑Δ จࣈྻ͔Βྻڍܕʹม͑Δ

Slide 24

Slide 24 text

const TIMESTAMP_FORMAT = 'Y-m-d\TH:i:s.uP'; public function findAll(\DateTime $startAt, \DateTime $endAt, Status $status): array { if (!in_array($status, ['OPEN', 'NEW', 'FIXED'], true)) { throw new \InvalidArgumentException('params["status"] should be in "OPEN","NEW","FIXED"'); } $sql = 'SELECT bug_id, summary, reported_at FROM Bugs WHERE reported_at >= :startAt AND reported_at < :endAt AND status = :status'; $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':startAt', $startAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':endAt', $endAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':status', $status->value, \PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(\PDO::FETCH_CLASS, Bug::class); } ૝ఆ͠ͳ͚Ε͹ͳΒͳ͍ঢ়گ͕͞Βʹݮͬͨ

Slide 25

Slide 25 text

Agenda  ܕએݴ  ྻڍܕ  ੩తղੳ  ܕͷϞσϦϯά  ෆมੑ  ׬શੑ  ੹຿ͷ഑ஔ 👉

Slide 26

Slide 26 text

ίʔυΛಈ͔ͣ͞ʹ ݕࠪ͢Δ

Slide 27

Slide 27 text

wݱࡏɺݴޠ΍ίϯύΠϥɺ੩తղੳπʔϧΛΊ ͙Δঢ়گ͸౰࣌ͱ͸େ͖͘ҟͳ͍ͬͯ·͢ɻϝ ϞϦ΍$16࣌ؒ͸૬ରతʹʮ͍҆ʯ΋ͷʹͳ ΓɺίϯύΠϥࣗ਎͕νΣοΫͰ͖ΔΤϥʔ΋ େ෯ʹ૿͑·ͨ͠ɻ wίʔυͷ඼࣭޲্ʹ͋ͨͬͯ͸ɺςετ͚ͩʹ པΔͷͰ͸ͳ͘ɺղੳπʔϧ΋ੵۃతʹར༻͢ ΂͖Ͱ͠ΐ͏ɻ ΤοηΠίʔυ෼ੳπʔϧΛར༻͢Δ IUUQTXXXPSFJMMZDPKQCPPLT

Slide 28

Slide 28 text

ݱঢ়ͷίʔυʹ1)14UBOΛ͔͚ͯΈΔ IUUQTQIQTUBOPSH

Slide 29

Slide 29 text

1)1͋Δ͋Δ໭Γ஋͕ΦϒδΣΫτ·ͨ͸GBMTF public function findAll(\DateTime $startAt, \DateTime $endAt, Status $status): array { $sql = 'SELECT bug_id, summary, reported_at FROM Bugs WHERE reported_at >= :startAt AND reported_at < :endAt AND status = :status'; $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':startAt', $startAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':endAt', $endAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':status', $status->value, \PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(\PDO::FETCH_CLASS, Bug::class); } $BOOPUDBMMNFUIPEFYFDVUF PO1%04UBUFNFOUcGBMTF $BOOPUDBMMNFUIPECJOE7BMVF PO1%04UBUFNFOUcGBMTF $BOOPUDBMMNFUIPEGFUDI"MM PO1%04UBUFNFOUcGBMTF ੩తղੳΛߦΘͳ͍ͱؾ͖ͮʹ͍͘

Slide 30

Slide 30 text

1%0QSFQBSFͷ໭Γ஋ IUUQTXXXQIQOFUNBOVBMKBQEPQSFQBSFQIQ ܕఆ্ٛ͸ԼҐޓ׵ੑͷͨΊGBMTFͷՄೳੑ΋දݱ͞Ε͍ͯΔ͕ 1)1Ҏ߱͸1%0&33.0%&@&9$&15*0/͕σϑΥϧτͳͷͰ GBMTF͕ฦͬͯ͘Δ͜ͱ͸ݱ࣮తʹ͸ߟ͑ʹ͍͘

Slide 31

Slide 31 text

1)1ͷBTTFSUͰܕΛڱΊΔ IUUQTQIQTUBOPSHXSJUJOHQIQDPEFOBSSPXJOHUZQFT

Slide 32

Slide 32 text

1)1ͷBTTFSUͰܕΛڱΊΔ public function findAll(\DateTime $startAt, \DateTime $endAt, Status $status): array { $sql = 'SELECT bug_id, summary, reported_at FROM Bugs WHERE reported_at >= :startAt AND reported_at < :endAt AND status = :status'; $stmt = $this->pdo->prepare($sql); assert($stmt !== false, 'PDO::ATTR_ERRMODE should be PDO::ERRMODE_EXCEPTION'); $stmt->bindValue(':startAt', $startAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':endAt', $endAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':status', $status->value, \PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(\PDO::FETCH_CLASS, Bug::class); } TUNUͷܕ͕1%04UBUFNFOUʹߜΒΕΔ ͜͜Ͱ͸TUNUͷܕ͕1%04UBUFNFOUcGBMTF

Slide 33

Slide 33 text

1)1ͷBTTFSU IUUQQIQOFUNBOVBMKBGVODUJPOBTTFSUQIQ

Slide 34

Slide 34 text

Agenda  ܕએݴ  ྻڍܕ  ੩తղੳ  ܕͷϞσϦϯά  ෆมੑ  ׬શੑ  ੹຿ͷ഑ஔ 👉

Slide 35

Slide 35 text

ܕͷϞσϦϯάͰ ޡΓ΍͢͞ΛݮΒ͢

Slide 36

Slide 36 text

public function testFindAll(): void { $xmas2025 = new \DateTime('2025-12-25'); $xmas2026 = new \DateTime('2026-12-25'); $bugs = $this->repo->findAll( $xmas2025, $xmas2026, Status::New ); $this->assertCount(3, $bugs); } ར༻ଆͷίʔυ Ҿ਺͕ͭ͋ΔͷͰ ͦΕͧΕͷ໾ׂ͕Θ͔Γʹ͍͘

Slide 37

Slide 37 text

public function testFindAll(): void { $xmas2025 = new \DateTime('2025-12-25'); $xmas2026 = new \DateTime('2026-12-25'); $bugs = $this->repo->findAll( startAt: $xmas2025, endAt: $xmas2026, status: Status::New ); $this->assertCount(3, $bugs); } ໊લ෇͖Ҿ਺ͰՄಡੑ͸޲্͕ͨ͠ɺ ݕࡧൣғʹFOE"Uؚ͕·ΕΔͷ͔ ؚ·Εͳ͍ͷ͔͕఻ΘΒͳ͍ ໊લ෇͖Ҿ਺ͰՄಡੑΛ্͛Δ 1 ) 1 

Slide 38

Slide 38 text

public function testFindAll(): void { $xmas2025 = new \DateTime('2025-12-25'); $xmas2026 = new \DateTime('2026-12-25'); $bugs = $this->repo->findAll( startAt: $xmas2025, endAtExclusive: $xmas2026, status: Status::New ); $this->assertCount(3, $bugs); } FOE"Uؚ͕·ΕΔ͔Ͳ͏͔Λ໊લͰࣔͯ͠΋ͬ͘͠Γ͜ͳ͍ Ҿ਺ͷ໊લͰࣔͯ͠΋ྑ͍͕ɺ ͜ͷઃܭ͸ৗʹద੾ͩΖ͏͔ʁ TUBSU"Uͷํ͸Ͳ͏ͳΔͩΖ͏͔ʁ

Slide 39

Slide 39 text

wྑ͍ΠϯλϑΣʔεͱ͸࣍ͷͭͷ৚݅Λຬͨ ͢ΠϯλϑΣʔε wਖ਼͘͠࢖༻͢Δํ͕ૢ࡞ϛεΛ͢ΔΑΓ؆୯ wޡͬͨ࢖͍ํΛ͢Δ͜ͱ͕ࠔ೉ ΤοηΠਖ਼͍͠࢖͍ํΛ؆୯ʹɺޡͬͨ࢖͍ํΛࠔ೉ʹ IUUQTXXXPSFJMMZDPKQCPPLT ΋ͬͱޡΓ΍͢͞ΛݮΒ͢ઃܭΛ͍ͨ͠

Slide 40

Slide 40 text

ର৅ͷྖҬʢυϝΠϯʣ ʹֶ͍ͭͯͼɺ ܕͷϞσϦϯάʹΑͬͯ ޡΓ΍͢͞ΛݮΒ͢

Slide 41

Slide 41 text

ൣғʢ۠ؒʣʹֶ͍ͭͯͿ୺఺ IUUQTKBXJLJQFEJBPSHXJLJ&$#"&@&#&"%"

Slide 42

Slide 42 text

ൣғʢ۠ؒʣʹֶ͍ͭͯͿด۠ؒɺ։۠ؒ IUUQTXFLBOB[BXBJUBDKQNBUIDBUFHPSZPUIFSTZVVHPVIFOLBOUFYDHJ UBSHFUNBUIDBUFHPSZPUIFSTZVVHPVLVLBOOIUNM

Slide 43

Slide 43 text

ൣғʢ۠ؒʣʹֶ͍ͭͯͿ൒։۠ؒ IUUQTXFLBOB[BXBJUBDKQNBUIDBUFHPSZPUIFSTZVVHPVIFOLBOUFYDHJ UBSHFUNBUIDBUFHPSZPUIFSTZVVHPVLVLBOOIUNM

Slide 44

Slide 44 text

final readonly class DateTimeEndpoint { public function __construct( public \DateTime $value, public bool $inclusive, ) {} } final readonly class DateTimeRange { public function __construct( public DateTimeEndpoint $startAt, public DateTimeEndpoint $endAt, ) {} } fi OBMSFBEPOMZDMBTTͱ $POTUSVDUPS1SPQFSUZ1SPNPUJPO ͷ૊Έ߹Θͤ͸1)1ʹ͓͚Δ3FDPSEܕͷ Α͏ͳ΋ͷͱݴ͑ͦ͏ লུͤ͞ͳ͍ʢσϑΥϧτ஋Λ༻ҙ͠ͳ͍ʣ͜ͱͰ ୺఺Λҙࣝ͠΍͘͢ͳΔ جૅͱͳΔܕΛ͍ͭͬͯ͘͘ 1 ) 1   fi OBMͰܧঝΛېࢭ͢Δ SFBEPOMZͰϓϩύςΟมߋΛېࢭ͢Δ

Slide 45

Slide 45 text

͜ΕͰؒҧ͍ʹ͘͘ͳ͕ͬͨɺࠓ౓͸࢖͏ͷ͕΍΍໘౗ʜʜ public function testFindAll(): void { $startAt = new DateTimeEndpoint( value: new \DateTime('2025-12-25'), inclusive: true ); $endAt = new DateTimeEndpoint( value: new \DateTime('2026-12-25'), inclusive: false ); $range = new DateTimeRange($startAt, $endAt); $bugs = $this->repo->findAll(searchRange: $range, status: Status::New); $this->assertCount(3, $bugs); }

Slide 46

Slide 46 text

wྑ͍ΠϯλϑΣʔεͱ͸࣍ͷͭͷ৚݅Λຬͨ ͢ΠϯλϑΣʔε wਖ਼͘͠࢖༻͢Δํ͕ૢ࡞ϛεΛ͢ΔΑΓ؆୯ wޡͬͨ࢖͍ํΛ͢Δ͜ͱ͕ࠔ೉ ΤοηΠਖ਼͍͠࢖͍ํΛ؆୯ʹɺޡͬͨ࢖͍ํΛࠔ೉ʹ IUUQTXXXPSFJMMZDPKQCPPLT ݱঢ়Ͱ͸ਖ਼͘͠࢖͏ͷ͕΍΍໘౗

Slide 47

Slide 47 text

खݎ͞ͱॻ͖΍͢͞ͷཱ྆ΛࢼΈΑ͏ final readonly class DateTimeEndpoint { (中略) public static function including(string $dateTimeStr): DateTimeEndpoint { return new DateTimeEndpoint( value: new \DateTime($dateTimeStr), inclusive: true ); } public static function excluding(string $dateTimeStr): DateTimeEndpoint { return new DateTimeEndpoint( value: new \DateTime($dateTimeStr), inclusive: false ); } } ෼͔Γ΍໊͍͢લͷϑΝΫτϦʔϝιουΛఆٛ ෼͔Γ΍໊͍͢લͷϑΝΫτϦʔϝιουΛఆٛ ʢ ෬ ઢ ʣ

Slide 48

Slide 48 text

͜ΕͰར༻ଆΛ୹͘ॻ͚ΔΑ͏ʹͳͬͨ public function testFindAll(): void { $range = new DateTimeRange( startAt: DateTimeEndpoint::including('2025-12-25'), endAt: DateTimeEndpoint::excluding('2026-12-25') ); $bugs = $this->repo->findAll(searchRange: $range, status: Status::New); $this->assertCount(3, $bugs); } ୹͘ॻ͚ΔΑ͏ʹͳͬͨ ʢ ෬ ઢ ʣ

Slide 49

Slide 49 text

Agenda  ܕએݴ  ྻڍܕ  ੩తղੳ  ܕͷϞσϦϯά  ෆมੑ  ׬શੑ  ੹຿ͷ഑ஔ 👉

Slide 50

Slide 50 text

ෆมੑ JNNVUBCJMJUZ  ঢ়ଶมԽΛഉআͯ͠ ޡΓ΍͢͞ΛݮΒ͢

Slide 51

Slide 51 text

ผ໊ࢀর໰୊ "MJBTJOH

Slide 52

Slide 52 text

4VCTDSJQUJPOΦϒδΣΫτΛྫʹผ໊ࢀর໰୊ΛֶͿ final readonly class Subscription implements \Stringable { public function __construct( private string $name, private DateTimeRange $range, ) { } public function __toString(): string { $startAt = $this->range->startAt->value; $endAt = $this->range->endAt->value; return $this->name . '(' . $startAt->format('Y-m-d') . ' -> ' . $endAt->format('Y-m-d') . ')'; } public function renew(): void { $oneYear = \DateInterval::createFromDateString('1 year'); $this->range->startAt->value->add($oneYear); $this->range->endAt->value->add($oneYear); } } ઌ΄Ͳ࡞੒ͨ͠%BUF5JNF3BOHFΦϒδΣΫτΛ࢖ͬͯΈΔ # " % αϒεΫϦϓγϣϯܖ໿Λ೥ߋ৽͢Δϝιου

Slide 53

Slide 53 text

$year2025 = new DateTimeRange( startAt: DateTimeEndpoint::including('2025-01-01'), endAt: DateTimeEndpoint::excluding('2026-01-01') ); $phpstorm = new Subscription('PhpStorm', $year2025); echo $phpstorm, PHP_EOL; // PhpStorm(2025-01-01 -> 2026-01-01) $oreilly = new Subscription('O\'Reilly', $year2025); echo $oreilly, PHP_EOL; // O'Reilly(2025-01-01 -> 2026-01-01) $phpstorm->renew(); echo $phpstorm, PHP_EOL; // PhpStorm(2026-01-01 -> 2027-01-01) echo $oreilly, PHP_EOL; // O'Reilly(2026-01-01 -> 2027-01-01) echo $year2025->startAt->value->format('Y-m-d H:i:s'), PHP_EOL; // 2026-01-01 00:00:00 echo $year2025->endAt->value->format('Y-m-d H:i:s'), PHP_EOL; // 2027-01-01 00:00:00 ผ໊ࢀর໰୊ʢ"MJBTJOHʣ ͳ͔ͥΦϥΠϦʔֶशϓϥοτϑΥʔϜͷ ܖ໿ظؒ΋ߋ৽͞Εͯ͠·͍ͬͯΔ  1IQ4UPSNͷαϒεΫϦϓγϣϯܖ໿Λ೥ߋ৽͢Δ

Slide 54

Slide 54 text

fi OBMSFBEPOMZએݴ͚ͩͰ͸ μϝͩͬͨͷ͸ͳ͔ͥ

Slide 55

Slide 55 text

Կ͕ݪҼͩͬͨͷ͔ # " % ͕͜͜໰୊ ࠶୅ೖ͠ͳ͘ͱ΋ഁյతมߋ͕Ͱ͖ͯ͠·͏ final readonly class Subscription implements \Stringable { public function __construct( private string $name, private DateTimeRange $range, ) { } public function __toString(): string { $startAt = $this->range->startAt->value; $endAt = $this->range->endAt->value; return $this->name . '(' . $startAt->format('Y-m-d') . ' -> ' . $endAt->format('Y-m-d') . ')'; } public function renew(): void { $oneYear = \DateInterval::createFromDateString('1 year'); $this->range->startAt->value->add($oneYear); $this->range->endAt->value->add($oneYear); } }

Slide 56

Slide 56 text

1)1ͷ%BUF5JNFΫϥε͕ഁյతมߋΛڐͯ͠͠·͏ 1)1ͷ%BUF5JNFΫϥε͕ ഁյతมߋΛڐͯ͠͠·͍ͬͯͨ ՄมΦϒδΣΫτ΁ͷࢀর͕औಘͰ͖Δͱ fi OBMSFBEPOMZͰ͋ͬͯ΋ঢ়ଶΛมߋͰ͖ͯ͠·͏ # " % final readonly class DateTimeEndpoint { public function __construct( public \DateTime $value, public bool $inclusive, ) {} final readonly class DateTimeRange { public function __construct( public DateTimeEndpoint $startAt, public DateTimeEndpoint $endAt, ) {} (後略)

Slide 57

Slide 57 text

ෆมΦϒδΣΫτΛͭ͘Δ

Slide 58

Slide 58 text

%BUF5JNFͱ%BUF5JNF*NNVUBCMF IUUQTXXXQIQOFUNBOVBMKBDMBTTEBUFUJNFQIQ

Slide 59

Slide 59 text

use PhpconFuk\PHP8_2\Immutable; #[Immutable] final readonly class DateTimeEndpoint { public function __construct( public \DateTime $value, public bool $inclusive, ) {} public static function including(string $dateTimeStr): DateTimeEndpoint { return new DateTimeEndpoint( value: new \DateTime($dateTimeStr), inclusive: true ); } public static function excluding(string $dateTimeStr): DateTimeEndpoint { return new DateTimeEndpoint( value: new \DateTime($dateTimeStr), inclusive: false ); } } .VUBCMFͳ%BUF5JNF&OEQPJOU # " %

Slide 60

Slide 60 text

use PhpconFuk\Immutable; #[Immutable] final readonly class DateTimeEndpoint { public function __construct( public \DateTimeImmutable $value, public bool $inclusive, ) {} public static function including(string $dateTimeStr): DateTimeEndpoint { return new DateTimeEndpoint( value: new \DateTimeImmutable($dateTimeStr), inclusive: true ); } public static function excluding(string $dateTimeStr): DateTimeEndpoint { return new DateTimeEndpoint( value: new \DateTimeImmutable($dateTimeStr), inclusive: false ); } } *NNVUBCMFͳ%BUF5JNF&OEQPJOU %BUF5JNF*NNVUBCMFΛ࢖͍ͬͯ͘ %BUF5JNF*NNVUBCMFΛ࢖͍ͬͯ͘ %BUF5JNF*NNVUBCMFΛ࢖͍ͬͯ͘

Slide 61

Slide 61 text

namespace PhpconFuk; use Attribute; #[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] class Immutable {} Ԡ༻ಠࣗͷ"UUSJCVUFΛఆٛͯ͠ҙຯ͚ͮΛ͠Α͏ IUUQTXXXBNB[PODPKQEQ *NNVUBCMFͰ͋Δ͜ͱΛࣔ͢ ΞτϦϏϡʔτΛಠࣗఆٛ͢Δ

Slide 62

Slide 62 text

Ԡ༻ಠࣗͷ"UUSJCVUFΛఆٛͯ͠ҙຯ͚ͮΛ͠Α͏ IUUQTXXXBNB[PODPKQEQ

Slide 63

Slide 63 text

use PhpconFuk\Immutable; #[Immutable] final readonly class DateTimeEndpoint { public function __construct( public \DateTimeImmutable $value, public bool $inclusive, ) {} () public static function including(string $dateTimeStr): DateTimeEndpoint { return new DateTimeEndpoint( value: new \DateTimeImmutable($dateTimeStr), inclusive: true ); } public static function excluding(string $dateTimeStr): DateTimeEndpoint { return new DateTimeEndpoint( value: new \DateTimeImmutable($dateTimeStr), inclusive: false ); } } *NNVUBCMFͰ͋Δͱʢ1)1Ͱ͸ͳ͘νʔϜʹʣ఻͑Δ ͖͞΄Ͳఆٛͨ͠*NNVUBCMF ΞτϦϏϡʔτΛ࢖͏

Slide 64

Slide 64 text

use PhpconFuk\Immutable; #[Immutable] final readonly class DateTimeEndpoint { public function __construct( public \DateTimeImmutable $value, public bool $inclusive, ) {} #[\NoDiscard] public function withValue(\DateTimeImmutable $newValue): DateTimeEndpoint { return clone($this, ["value" => $newValue]); } (後略) ෆมΦϒδΣΫτͷΫϩʔϯੜ੒ 1)1Ͱ͸ෆมΦϒδΣΫτͷҰ෦͚ͩͷ஋Λ มߋͨ͠ΫϩʔϯΛ࡞ΕΔΑ͏ʹͳͬͨ 1)1Ͱ௥Ճ͞Εͨ ໭Γ஋Λແࢹͤ͞ͳ͍ /P%JTDBSEΞτϦϏϡʔτΛ෇༩ 1 ) 1  

Slide 65

Slide 65 text

final readonly class Subscription implements \Stringable { public function __construct( private string $name, private DateTimeRange $range, ) { } (中略) public function renew(): void { $oneYear = \DateInterval::createFromDateString('1 year'); $this->range->startAt->value->add($oneYear); $this->range->endAt->value->add($oneYear); } .VUBCMFͳ4VCTDSJQUJPO %BUF5JNFΦϒδΣΫτͷ ഁյతมߋΛલఏʹ͍ͯͨ͠ มߋલ͸%BUF5JNF3BOHF͕อ࣋͢Δ %BUF5JNF&OEQPJOU͕NVUBCMFͩͬͨ # " %

Slide 66

Slide 66 text

use PhpconFuk\Immutable; #[Immutable] final readonly class Subscription implements \Stringable { public function __construct( private string $name, private DateTimeRange $range, ) { } (中略) #[\NoDiscard] public function renew(): Subscription { $oneYear = \DateInterval::createFromDateString('1 year'); $startAt = $this->range->startAt; $endAt = $this->range->endAt; return new Subscription( name: $this->name, range: new DateTimeRange( startAt: $startAt->withValue($startAt->value->add($oneYear)), endAt: $endAt->withValue($endAt->value->add($oneYear)) ) ); } *NNVUBCMFͳ4VCTDSJQUJPO ࣗ਎ͷঢ়ଶΛมߋͤͣɺ ৽͍͠ঢ়ଶΛอ࣋ͨ͠ෆมΦϒδΣΫτΛฦ͢ %BUF5JNF3BOHF͕อ࣋͢Δ %BUF5JNF&OEQPJOU͕อ࣋͢Δ೔෇ܕ͕ %BUF5JNF*NNVUBCMFʹมߋ͞Εͨ ໭Γ஋Λແࢹͤ͞ͳ͍ /P%JTDBSEΞτϦϏϡʔτΛ෇༩ 1 ) 1   ্୺఺ɺԼ୺఺͸ઌ΄Ͳఆٛͨ͠XJUI7BMVFϝιουͰ ෆมΫϩʔϯΛ࡞੒

Slide 67

Slide 67 text

ෆมΦϒδΣΫτͰͷ ໰୊ղܾ

Slide 68

Slide 68 text

$year2025 = new DateTimeRange( startAt: DateTimeEndpoint::including('2025-01-01'), endAt: DateTimeEndpoint::excluding('2026-01-01') ); $phpstorm = new Subscription('PhpStorm', $year2025); echo $phpstorm, PHP_EOL; // PhpStorm(2025-01-01 -> 2026-01-01) $oreilly = new Subscription('O\'Reilly', $year2025); echo $oreilly, PHP_EOL; // O'Reilly(2025-01-01 -> 2026-01-01) $phpstorm->renew(); // Warning: The return value of method PhpconFuk\Subscription::renew() should either be used or intentionally ignored by casting it as (void) in /path/to/demo_aliasing.php echo $oreilly, PHP_EOL; // O'Reilly(2025-01-01 -> 2026-01-01) echo $year2025->startAt->value->format('Y-m-d H:i:s'), PHP_EOL; // 2025-01-01 00:00:00 echo $year2025->endAt->value->format('Y-m-d H:i:s'), PHP_EOL; // 2026-01-01 00:00:00 ෆมΦϒδΣΫτʹΑͬͯผ໊ࢀর໰୊Λղܾ ໭Γ஋Λແࢹ͢Δͱ/P%JTDBSEΞτϦϏϡʔτʹΑͬͯܯࠂ͕ग़Δ

Slide 69

Slide 69 text

$year2025 = new DateTimeRange( startAt: DateTimeEndpoint::including('2025-01-01'), endAt: DateTimeEndpoint::excluding('2026-01-01') ); $phpstorm = new Subscription('PhpStorm', $year2025); echo $phpstorm, PHP_EOL; // PhpStorm(2025-01-01 -> 2026-01-01) $oreilly = new Subscription('O\'Reilly', $year2025); echo $oreilly, PHP_EOL; // O'Reilly(2025-01-01 -> 2026-01-01) $phpstorm2026 = $phpstorm->renew(); echo $phpstorm2026, PHP_EOL; // PhpStorm(2026-01-01 -> 2027-01-01) echo $phpstorm, PHP_EOL; // PhpStorm(2025-01-01 -> 2026-01-01) echo $oreilly, PHP_EOL; // O'Reilly(2025-01-01 -> 2026-01-01) echo $year2025->startAt->value->format('Y-m-d H:i:s'), PHP_EOL; // 2025-01-01 00:00:00 echo $year2025->endAt->value->format('Y-m-d H:i:s'), PHP_EOL; // 2026-01-01 00:00:00 ෆมΦϒδΣΫτʹΑͬͯผ໊ࢀর໰୊Λղܾ 👍 ৽ͨͳαϒεΫϦϓγϣϯظؒΛ൐͏৽ͨͳΦϒδΣΫτΛฦ͢ ݩͷΦϒδΣΫτͨͪ͸ෆมͷ·· ৽ͨͳαϒεΫϦϓγϣϯظؒΛ൐͏৽ͨͳΦϒδΣΫτΛฦ͢

Slide 70

Slide 70 text

Agenda  ܕએݴ  ྻڍܕ  ੩తղੳ  ܕͷϞσϦϯά  ෆมੑ  ׬શੑ  ੹຿ͷ഑ஔ 👉

Slide 71

Slide 71 text

׬શੑ JOUFHSJUZ  ଘࡏ͢ΔͳΒ͹ৗʹਖ਼͍͠ ʢෆม৚͕݅ৗʹ੒Γཱͭʣ ঢ়ଶͱͳΔઃܭΛߦ͍ ޡΓ΍͢͞ΛݮΒ͢

Slide 72

Slide 72 text

ݕࡧ։࢝೔࣌ͱ ݕࡧऴྃ೔࣌ͷ ੔߹ੑΛ୲อ͢Δͷ͸ ୭ͷ੹೚

Slide 73

Slide 73 text

public function findAll(DateTimeRange $searchRange, Status $status): array { $startAt = $searchRange->startAt->value; $endAt = $searchRange->endAt->value; if ($endAt < $startAt) { throw new \InvalidArgumentException('endAt < startAt'); } $startAtOp = $searchRange->startAt->inclusive ? '<=' : '<'; $endAtOp = $searchRange->endAt->inclusive ? '<=' : '<'; $sql = "SELECT bug_id, summary, reported_at FROM Bugs WHERE status = :status AND :startAt ${startAtOp} reported_at AND reported_at ${endAtOp} :endAt"; $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':status', $status->value, \PDO::PARAM_STR); $stmt->bindValue(':startAt', $startAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->bindValue(':endAt', $endAt->format(self::TIMESTAMP_FORMAT), \PDO::PARAM_STR); $stmt->execute(); return $stmt->fetchAll(\PDO::FETCH_CLASS, Bug::class); } ͜͜Ͱ΍Δͷ͸ے͕ѱ͍ fi OE"MMͷೖΓޱͰߦ͏ͷ͸ے͕ѱ͍

Slide 74

Slide 74 text

ଘࡏ͢ΔͳΒ͹طʹਖ਼͍͠ઃܭΛ࡞Δώϯτ .BLFJMMFHBMTUBUFTVOSFQSFTFOUBCMF %F fi OFFSSPSTPVUPGFYJTUFODF 'BJMGBTU ͭͷҟͳΔߟ͑ํ͕͋Δ ʢ˞Ұൠతʹɺ%F fi OFFSSPSTPVUPGFYJTUFODFͱ 'BJMGBTU͸ཱ྆Ͱ͖ͳ͍ঢ়گ͕ଟ͍ʣ

Slide 75

Slide 75 text

.BLFJMMFHBMTUBUFT VOSFQSFTFOUBCMF ෆਖ਼ͳঢ়ଶ͸ දݱͰ͖ͳ͍Α͏ʹ͢Δ

Slide 76

Slide 76 text

.BLFJMMFHBMTUBUFTVOSFQSFTFOUBCMF ͜Ε͸ɺʮෆਖ਼ͳঢ়ଶ͸දݱͰ͖ͳ͍Α͏ʹ͢Δʯͱ͍͏ॏཁͳઃܭΨΠυ ϥΠϯͷҰྫͰ͢ɻࢲͨͪ͸ϏδωεϧʔϧΛܕγεςϜͰදݱ͠Α͏ͱ͠ ͍ͯ·͢ɻ͜Ε͕ద੾ʹߦΘΕ͍ͯΕ͹ɺෆਖ਼ͳঢ়ଶ͕ίʔυʹଘࡏ͢Δ͜ ͱ͸ͳ͘ɺͦͷͨΊͷϢχοτςετΛॻ͘ඞཁ͸͋Γ·ͤΜɻͦͷ୅ΘΓ ʹͳΔͷ͕ɺʮίϯύΠϧ࣌ʯͱ͍͏ϢχοτςετͳͷͰ͢ɻ IUUQTXXXBNB[PODPKQEQ#%%#1/#7+

Slide 77

Slide 77 text

.BLFJMMFHBMTUBUFTVOSFQSFTFOUBCMF w ܕͷઃܭʹΑͬͯɺෆਖ਼ͳঢ়ଶͰ͸ͦ΋ͦ΋ίϯύΠϧʢ1)1ͷ৔߹͸ ੩తղੳʣ͕௨Βͳ͍Α͏ʹ͍ͨ͠ w ࠓճͷ৔߹͸Ҿ਺TUBSU"UΑΓҾ਺FOE"Uͷํ͕େ͖͍ʢະདྷ೔ʣ ͜ͱΛܕͰදݱ͍͕ͨ͠ʜʜ w 3F fi OFNFOU5ZQFT΍%FQFOEFOU5ZQFTͷΑ͏ͳܕͷදݱྗ͕ඞཁ ͳͷͰɺকདྷͷ1)1ʹظ଴ w ʢࠓճͷαϯϓϧʹ͸͏·͘߹க͠·ͤΜͰͨ͠ʣ

Slide 78

Slide 78 text

%F fi OFFSSPSTPVUPG FYJTUFODF ΤϥʔΛఆٛͰফ͠ڈΔ

Slide 79

Slide 79 text

ྫ֎ॲཧͷෳࡶ͞Λഉআ͢Δ࠷ྑͷํ๏͸ɺॲཧ͢΂͖ྫ֎͕ଘࡏ͠ͳ͍Α ͏ʹ"1*Λఆٛ͢Δ͜ͱͰ͢ɻ ͭ·ΓɺΤϥʔΛఆٛͰফ͠ڈΔͷͰ͢ɻ͜Ε͸๯ಚతʹฉ͑͜Δ͔΋͠Ε ·ͤΜ͕ɺ࣮ફʹ͓͍ͯ͸ඇৗʹޮՌతͰ͢ɻ %F fi OFFSSPSTPVUPGFYJTUFODF IUUQTXXXBNB[PODPKQEQ9

Slide 80

Slide 80 text

খ͍͞ํͷ஋ΛԼ୺఺ɺେ͖͍ํͷ஋Λ্୺఺ʹ͢Ε͹͔֬ʹΤϥʔ৚݅͸ͳ͘ͳΔ final readonly class DateTimeRange { private function __construct( public DateTimeEndpoint $startAt, public DateTimeEndpoint $endAt, ) { } public static function createFromEndpoints( DateTimeEndpoint $endpoint1, DateTimeEndpoint $endpoint2 ): self { return $endpoint1->value <= $endpoint2->value ? new self($endpoint1, $endpoint2) : new self($endpoint2, $endpoint1); } } Ҿ਺ͷେখؔ܎Λ൑ఆ͠ɺ খ͍͞ํΛԼ୺఺ʹ͢Δ ϑΝΫτϦʔϝιουΛఆٛ ෆ੔߹ͳঢ়ଶΛ࡞Βͳ͍ͨΊ ίϯετϥΫλͷՄࢹੑΛ QSJWBUFʹͯ͠ ௚઀ݺ΂ͳ͍Α͏ʹ͢Δ ˞͜ͷΑ͏ͳઃܭ͸ࢍ൱྆࿦ ͋Δͱࢥ͍·͢ɻٞ࿦ͷ͖ͬ ͔͚ʹ͍ͯͩ͘͠͞

Slide 81

Slide 81 text

%F fi OFFSSPSTPVUPGFYJTUFODF ΤϥʔΛଟ༻͢ΔΞϓϩʔν͸͍͔ͭ͘ͷόάΛัଊͰ͖Δ͔΋͠Ε·ͤΜ ͕ɺಉ࣌ʹෳࡶ͞΋૿େͤ͞ɺͦΕ͕ଞͷόάΛੜΈग़݁͢ՌͱͳΓ·͢ɻ ΤϥʔΛଟ༻͢ΔΞϓϩʔνͰ͸ɺ։ൃऀ͸ΤϥʔΛճආ·ͨ͸ແࢹ͢Δͨ Ίͷ௥ՃίʔυΛॻ͔ͳ͚Ε͹ͳΒͣɺ͜Ε͕όάͷՄೳੑΛߴΊ·͢ɻ͋ Δ͍͸ɺ௥ՃίʔυΛॻ͘͜ͱΛ๨Εͯ͠·͍ɺͦͷ৔߹͸࣮ߦ࣌ʹ༧ظ͠ ͳ͍Τϥʔ͕౤͛ΒΕΔՄೳੑ͕͋Γ·͢ɻ IUUQTXXXBNB[PODPKQEQ9

Slide 82

Slide 82 text

%F fi OFFSSPSTPVUPGFYJTUFODF ରরతʹɺΤϥʔΛఆٛͰফ͠ڈΔΞϓϩʔν͸"1*Λ؆ܿʹ͠ɺॻ͔ͳ͚ Ε͹ͳΒͳ͍ίʔυͷྔΛݮΒ͠·͢ɻ શମͱͯ͠ɺόάΛݮΒ͢࠷ྑͷํ๏͸ιϑτ΢ΣΞΛΑΓγϯϓϧʹ͢Δ ͜ͱͰ͢ɻ IUUQTXXXBNB[PODPKQEQ9

Slide 83

Slide 83 text

'BJMGBTU ෆਖ਼ͳঢ়ଶͳΒ͹ ଎΍͔ʹࣦഊͤ͞Δ

Slide 84

Slide 84 text

ୡਓϓϩάϥϚʔ5JQૣΊʹΫϥογϡͤ͞Δ͜ͱ ʮ͋Γಘͳ͍ʯͱࢥΘΕΔࣄ৅͕ίʔυͷ࣮ߦதʹൃੜͨ͠৔߹ɺͦͷ࣌ ఺ͰϓϩάϥϜ͸΋͸΍࣮ߦՄೳͳ΋ͷͱ͸ͳ͍ͬͯͳ͍ͷͰ͢ɻͦͷ࣌ ఺Ҏ߱ʹॲཧ͞Εͨ಺༰͸ٙΘ͍͠΋ͷͰ͋ΔͨΊɺ଎΍͔ʹఀࢭͤͯ͞ ͍ͩ͘͞ɻ ௨ৗͷ৔߹ɺఀࢭͨ͠ϓϩάϥϜͷ΄͏͕ɺো֐ʹΑͬͯத్൒୺ʹಈ࡞ ͍ͯ͠ΔϓϩάϥϜΑΓ΋μϝʔδ͸গͳ͍͸ͣͰ͢ IUUQTXXXPINTIBDPKQCPPL

Slide 85

Slide 85 text

final readonly class DateTimeRange { /** * 与えられた下端点と上端点からDateTimeRangeを生成します。 * 上端点は下端点以上である必要があります。 * @param DateTimeEndpoint $startAt 下端点 * @param DateTimeEndpoint $endAt 上端点 */ public function __construct( public DateTimeEndpoint $startAt, public DateTimeEndpoint $endAt, ) { assert($startAt->value <= $endAt->value, '上端点は下端点以上である必要があります'); } JOWBSJBOU ৗʹ੒Γཱͭ΂͖ෆม৚݅ ΛࣜͰॻ͘ ࣄલ৚݅Λ͖ͪΜͱ఻͑Δ͜ͱ ࣄલ৚݅ʢར༻ଆͷ੹೚ʣͰ͋Δ৔߹ɺද໌Λ࢖͏͔ɺར༻ଆͷ੹೚Ͱ͋Δ͜ͱΛࣔ͢ܕͷྫ֎Λ࢖͏

Slide 86

Slide 86 text

final readonly class DateTimeRange { /** * 与えられた下端点と上端点からDateTimeRangeを生成します。 * @param DateTimeEndpoint $startAt 下端点 * @param DateTimeEndpoint $endAt 上端点 * @throws \InvalidArgumentException 下端点が上端点より大きい場合 */ public function __construct( public DateTimeEndpoint $startAt, public DateTimeEndpoint $endAt, ) { if ($startAt->value > $endAt->value) { throw new \InvalidArgumentException( '上端点は下端点以上である必要があります'); } } ར༻ଆͷޡΓͰ͋Δࢫͷྫ֎Λൃੜͤ͞Δ ࣄલ৚݅Λ͖ͪΜͱ఻͑Δ͜ͱ ࣄલ৚݅ʢར༻ଆͷ੹೚ʣͰ͋Δ৔߹ɺද໌Λ࢖͏͔ɺར༻ଆͷ੹೚Ͱ͋Δ͜ͱΛࣔ͢ܕͷྫ֎Λ࢖͏

Slide 87

Slide 87 text

্୺఺ͱԼ୺఺͕౳͍͠ͱ͖͸ʁ IUUQTKBXJLJQFEJBPSHXJLJ&$#"&@&#&"%" େࣄͳΤοδέʔεͳͷͰ͕͢ɺࠓճͷߨԋͰ͸࣌ؒͷؔ܎ͰׂѪ͠·͢

Slide 88

Slide 88 text

Agenda  ܕએݴ  ྻڍܕ  ੩తղੳ  ܕͷϞσϦϯά  ෆมੑ  ׬શੑ  ੹຿ͷ഑ஔ 👉

Slide 89

Slide 89 text

ద੾ͳ৔ॴͰ ద੾ͳ࢓ࣄΛߦ͏

Slide 90

Slide 90 text

w จࣈྻ͔Β%BUF5JNF*NNVUBCMFʹม׵͢Δͷ͸୭ͷ੹೚ Ͳ͜Ͱ΍Δ  w %BUF5JNF*NNVUBCMF͔Βจࣈྻʹม׵͢Δͷ͸୭ͷ੹೚ Ͳ͜Ͱ΍Δ  w ʢ͍··Ͱݟͯݟ͵ৼΓΛ͍ͯͨ͠ʣλΠϜκʔϯΛѻ͏ͷ͸୭ͷ੹೚ Ͳ͜Ͱ΍Δ ੹຿ʹؔ͢Δઃܭͷٙ໰఺Λ·ͱΊ͍ͯ͜͏

Slide 91

Slide 91 text

จࣈྻ͔Β%BUF5JNF*NNVUBCMF΁ͷม׵Λ ෆద੾ͳ৔ॴͰߦ͍ͬͯΔ͜ͱʹؾ͍ͮͯ͠·ͬͨ

Slide 92

Slide 92 text

final readonly class DateTimeEndpoint { public function __construct( public \DateTimeImmutable $value, public bool $inclusive, ) {} (中略) public static function including(string $dateTimeStr): DateTimeEndpoint { return new DateTimeEndpoint( value: new \DateTimeImmutable($dateTimeStr), inclusive: true ); } public static function excluding(string $dateTimeStr): DateTimeEndpoint { return new DateTimeEndpoint( value: new \DateTimeImmutable($dateTimeStr), inclusive: false ); } } ઃܭͷෆ٢ͳष͍ʹؾ͍ͮͯ͠·ͬͨ # " % จࣈྻ͔Β%BUF5JNF*NNVUBCMF΁ͷม׵ʹ͸ ๲େͳ૊Έ߹Θ͕ͤ͋Δ͠ɺ λΠϜκʔϯͷѻ͍΋ෆे෼ʹͳͬͯ͠·͍ͬͯΔɻ จࣈྻҾ਺ʹλΠϜκʔϯͷΦϑηοτදهΛؚΊ ͳ͍ͱσϑΥϧτͷλΠϜκʔϯʹͳͬͯ͠·͏  ͭ·Γ͜͜Ͱߦ͏ͷ͸ෆద੾ɻ จࣈྻ͔Β%BUF5JNF*NNVUBCMF΁ͷม׵ʹ͸ ๲େͳ૊Έ߹Θ͕ͤ͋Δ͠ɺ λΠϜκʔϯͷѻ͍΋ෆे෼ʹͳͬͯ͠·͍ͬͯΔɻ จࣈྻҾ਺ʹλΠϜκʔϯͷΦϑηοτදهΛؚΊ ͳ͍ͱσϑΥϧτͷλΠϜκʔϯʹͳͬͯ͠·͏  ͭ·Γ͜͜Ͱߦ͏ͷ͸ෆద੾ɻ

Slide 93

Slide 93 text

public function testFindAll(): void { $range = new DateTimeRange( startAt: DateTimeEndpoint::including('2025-12-25'), endAt: DateTimeEndpoint::excluding('2026-12-25') ); $bugs = $this->repo->findAll(searchRange: $range, status: Status::New); $this->assertCount(3, $bugs); } ࢖͏ͷ͕΍΍໘౗ͩͬͨͷͰɺ୹͘ॻ͚ΔศརͳϝιουΛఏڙ͔ͨͬͨ͠ͷͩͬͨ ༰қ͞ʹدΓ͗ͯ͢ɺ ޡΓ΍͢͞΋্͕ͬͯ͠·͍ͬͯͳ͍ͩΖ͏͔ ςετίʔυͰ୹͘ॻ͖͍ͨͷͰ༰қ͞ʢ&BTZʣدΓʹόΠΞε͕͔͔ͬͯ͠·ͬͨɻ ςετίʔυͷେ෦෼Ͱ͸ಛʹλΠϜκʔϯΛҙࣝͤͣ୹͘ॻ͍͍͖͍͕ͯͨɺ ྫ͑͹8FCͷίϯτϩʔϥͰ͸λΠϜκʔϯΛҙࣝͯ͠ݫີʹѻ͍͍ͨɻ ςετίʔυ΋ͻͱͭͷίϯςΫετɻ ςετ͔ΒઃܭΛۦಈ͢Δͱ͖ʹ͜ͷόΠΞεʹ஫ҙ͢Δඞཁ͕͋Δɻ # " %

Slide 94

Slide 94 text

wྑ͍ΠϯλϑΣʔεͱ͸࣍ͷͭͷ৚݅Λຬͨ ͢ΠϯλϑΣʔε wਖ਼͘͠࢖༻͢Δํ͕ૢ࡞ϛεΛ͢ΔΑΓ؆୯ wޡͬͨ࢖͍ํΛ͢Δ͜ͱ͕ࠔ೉ ΤοηΠਖ਼͍͠࢖͍ํΛ؆୯ʹɺޡͬͨ࢖͍ํΛࠔ೉ʹ IUUQTXXXPSFJMMZDPKQCPPLT ༰қ͞ΛٻΊͨ݁Ռɺ ޡͬͨ࢖͍ํΛ͢Δͷ΋༰қʹͳͬͯ͠·ͬͨ ༰қ͞ΛٻΊͨ݁Ռɺ ޡͬͨ࢖͍ํΛ͢Δͷ΋༰қʹͳͬͯ͠·ͬͨ

Slide 95

Slide 95 text

4JNQMFͱ&BTZ͸ࠞͥΔͳةݥ IUUQTUXJUUFSDPNU@XBEBTUBUVT IUUQTUXJUUFSDPNU@XBEBTUBUVT

Slide 96

Slide 96 text

4JNQMFͱ&BTZΛ ࠞͥͳ͍ &BTZ͞͸ ঢ়گʹΑΓҟͳΔ

Slide 97

Slide 97 text

final readonly class DateTimeEndpoint { public function __construct( public \DateTimeImmutable $value, public bool $inclusive, ) {} (中略) public static function including(string $dateTimeStr): DateTimeEndpoint { return new DateTimeEndpoint( value: new \DateTimeImmutable($dateTimeStr), inclusive: true ); } public static function excluding(string $dateTimeStr): DateTimeEndpoint { return new DateTimeEndpoint( value: new \DateTimeImmutable($dateTimeStr), inclusive: false ); } } 4JNQMFͱ&BTZ͕ࠞͬͯ͟͠·͍ͬͯͨ Simple Easy # " %

Slide 98

Slide 98 text

final readonly class DateTimeEndpoint { public function __construct( public \DateTimeImmutable $value, public bool $inclusive, ) {} (中略) public static function including(\DateTimeImmutable $value): DateTimeEndpoint { return new DateTimeEndpoint( value: $value, inclusive: true ); } public static function excluding(\DateTimeImmutable $value): DateTimeEndpoint { return new DateTimeEndpoint( value: $value, inclusive: false ); } } جૅͱͳΔܕ͸4JNQMF͞Λอͭ จࣈྻ͔Βͷม׵ΛѻΘͳ͍ Simple 4 JN Q MF Simple

Slide 99

Slide 99 text

final class DateTimeShorthandHelper { public static function jst(string $dateTimeStr): \DateTimeImmutable { return new \DateTimeImmutable($dateTimeStr, new \DateTimeZone('Asia/Tokyo')); } public static function utc(string $dateTimeStr): \DateTimeImmutable { return new \DateTimeImmutable($dateTimeStr, new \DateTimeZone('UTC')); } } &BTZ͞͸ผͷϨΠϠʔͰఏڙ͢Δɻྫ͑͹ςετ༻ͷϔϧύʔ &BTZ

Slide 100

Slide 100 text

use PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\Test; use PhpconFuk\DateTimeShorthandHelper as DT; class DateTimeRangeTest extends TestCase { #[Test] public function dateTimeShorthand(): void { $range = new DateTimeRange( startAt: DateTimeEndpoint::including(DT::jst('2025-12-25')), endAt: DateTimeEndpoint::excluding(DT::jst('2026-12-25')) ); $this->assertSame('2025-12-25', $range->startAt->value->format('Y-m-d')); $this->assertSame('2026-12-25', $range->endAt->value->format('Y-m-d')); } } &BTZ͞͸ผͷϨΠϠʔͰఏڙ͢Δɻྫ͑͹ςετ༻ͷϔϧύʔ ςετίʔυ্Ͱ4JNQMFͱ&BTZΛ߹ྲྀͤ͞Δɻ ͜͏ॻ͚Ε͹े෼ͩͬͨɻ

Slide 101

Slide 101 text

1BSTF EPOU KVTU WBMJEBUF IUUQTMFYJMBNCEBHJUIVCJPCMPHQBSTFEPOUWBMJEBUF

Slide 102

Slide 102 text

1BSTF EPOU KVTU WBMJEBUFͷઆ໌ͷͨΊʹ ΈΜͳେ޷͖ʰ$MFBO"SDIJUFDUVSFʱͷ ಉ৺ԁΛྫʹઆ໌͢Δ

Slide 103

Slide 103 text

੺ͱ੨ͷք໘ɺ૚ͷք໘͕͋Δ ੨͸֎քͱͷγϦΞϥΠθʔγϣϯڥք

Slide 104

Slide 104 text

1BSTF ݕূࡁΈͷ஋Λܕͱͯ͠දݱ͠ɺ ͦͷޙ͸ܕͷอূʹཔΔ 7BMJEBUF ֎ք͔Βͷ༷ʑͳೖྗΛɺ ίϯςΫετʹԠͯ͡ద੾ʹݕূ͢Δ

Slide 105

Slide 105 text

DateTimeZone DateTimeImmutable string 8FC͔ΒདྷͨจࣈྻΛόϦσʔγϣϯͭͭ͠ɺ ʢΞϓϦέʔγϣϯͰద੾ʹอ࣋ͨ͠ʣλΠϜ κʔϯΛ൐ͬͯ%BUF5JNF*NNVUBCMFʹม׵͠ɺ ͦΕΛ͞Βʹ%BUF5JNF&OEQPJOU΍ %BUF5JNF3BOHFʹม׵͢Δ %BUF5JNF&OEQPJOU͔Βऔಘͨ͠ %BUF5JNF*NNVUBCMFΛ λΠϜκʔϯΦϑηοτΛ൐ͬͨ λΠϜελϯϓจࣈྻͱͯ͠%#ʹ౉͢ DateTimeRange DateTimeEndpoint string DateTimeImmutable DateTimeZone

Slide 106

Slide 106 text

͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ w ݎ࿚ͳίʔυͱ͸ѱ͍ίʔυʹឺ૑ߣΛ͋ͯΔ͜ͱͰ͸ͳ͍ w ܕએݴͰ૝ఆ͢΂͖ೖྗͷܕΛߜΓɺ๷ޚతνΣοΫΛେ෯ʹݮΒ͢ w ྻڍܕͰऔΓಘΔ஋ΛߜΓɺ๷ޚతνΣοΫΛେ෯ʹݮΒ͢ w ੩తղੳπʔϧΛ࢖͍ɺίʔυΛಈ͔ͣ͞ͱ΋ݕূͰ͖ΔΑ͏ʹ͢Δ w جૅͱͳΔܕΛϞσϦϯά͠ɺᐆດ͞΍ؒҧ͍΍͢͞Λ࡟ݮ͢Δ w جૅͱͳΔܕΛෆมΦϒδΣΫτʹ͠ɺঢ়ଶมԽ΍෭࡞༻ʹىҼ͢ΔόάΛ༧๷͢Δ w ΦϒδΣΫτੜ੒࣌ʹෆม৚݅Λ੒ཱͤ͞ɺ͔ͭͦͷΦϒδΣΫτ͕ෆมΦϒδΣΫτͰ͋Δͳ Β͹ɺੜ੒͞ΕͨΦϒδΣΫτ͸ৗʹਖ਼͍͠ʢ׬શੑʣ w 4JNQMF͞ʢ֓೦ͱͯ͠ͷཁૉͷগͳ͞ʣͱ&BTZ͞ʢख਺ͷগͳ͞ʣΛෆ༻ҙʹࠞͥͳ͍ w ݕূࡁΈͷ஋Λܕͱͯ͠දݱ͠ɺͦͷޙ͸ܕͷอূʹཔΔ w ઃܭͱ͸੹຿ͷ࠷ద഑ஔΛٻΊଓ͚Δ͜ͱɻ୭͕ԿΛ஌͍ͬͯͯԿΛ஌Δ΂͖Ͱͳ͍͔ɺԿΛ΍ Δ΂͖ͰԿΛ΍Δ΂͖Ͱͳ͍͔Λৗʹߟ͑ଓ͚Δ͜ͱ

Slide 107

Slide 107 text

ࢀߟจݙ