generated from spawnia/php-package-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
44 changed files
with
3,062 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace MLL\Utils\Microplate; | ||
|
||
use Illuminate\Support\Collection; | ||
use MLL\Utils\Microplate\Enums\FlowDirection; | ||
use MLL\Utils\Microplate\Exceptions\UnexpectedFlowDirection; | ||
|
||
/** | ||
* @template TWell | ||
* @template TCoordinateSystem of CoordinateSystem | ||
* | ||
* @phpstan-type WellsCollection Collection<string, TWell|null> | ||
*/ | ||
abstract class AbstractMicroplate | ||
{ | ||
public const EMPTY_WELL = null; | ||
|
||
/** @var TCoordinateSystem */ | ||
public CoordinateSystem $coordinateSystem; | ||
|
||
/** @param TCoordinateSystem $coordinateSystem */ | ||
public function __construct(CoordinateSystem $coordinateSystem) | ||
{ | ||
$this->coordinateSystem = $coordinateSystem; | ||
} | ||
|
||
/** @return WellsCollection */ | ||
abstract public function wells(): Collection; | ||
|
||
/** | ||
* @param Coordinates<TCoordinateSystem> $coordinate | ||
* | ||
* @return TWell|null | ||
*/ | ||
public function well(Coordinates $coordinate) | ||
{ | ||
return $this->wells()[$coordinate->toString()]; | ||
} | ||
|
||
/** @param Coordinates<TCoordinateSystem> $coordinate */ | ||
public function isWellEmpty(Coordinates $coordinate): bool | ||
{ | ||
return $this->well($coordinate) === self::EMPTY_WELL; | ||
} | ||
|
||
/** @return WellsCollection */ | ||
public function sortedWells(FlowDirection $flowDirection): Collection | ||
{ | ||
return $this->wells()->sortBy( | ||
/** | ||
* @param TWell $content | ||
*/ | ||
function ($content, string $key) use ($flowDirection): string { | ||
switch ($flowDirection->value) { | ||
case FlowDirection::ROW: | ||
return $key; | ||
case FlowDirection::COLUMN: | ||
$coordinates = Coordinates::fromString($key, $this->coordinateSystem); | ||
|
||
return $coordinates->column . $coordinates->row; | ||
// @codeCoverageIgnoreStart all Enums are listed and this should never happen | ||
default: | ||
throw new UnexpectedFlowDirection($flowDirection); | ||
// @codeCoverageIgnoreEnd | ||
} | ||
}, | ||
SORT_NATURAL | ||
); | ||
} | ||
|
||
/** @return Collection<string, null> */ | ||
public function freeWells(): Collection | ||
{ | ||
return $this->wells()->filter( | ||
/** | ||
* @param TWell $content | ||
*/ | ||
static fn ($content): bool => $content === self::EMPTY_WELL | ||
); | ||
} | ||
|
||
/** @return Collection<string, TWell> */ | ||
public function filledWells(): Collection | ||
{ | ||
return $this->wells()->filter( | ||
/** | ||
* @param TWell $content | ||
*/ | ||
static fn ($content): bool => $content !== self::EMPTY_WELL | ||
); | ||
} | ||
|
||
/** @return callable(TWell|null $content, string $coordinatesString): bool */ | ||
public function matchRow(string $row): callable | ||
{ | ||
return function ($content, string $coordinatesString) use ($row): bool { | ||
$coordinates = Coordinates::fromString($coordinatesString, $this->coordinateSystem); | ||
|
||
return $coordinates->row === $row; | ||
}; | ||
} | ||
|
||
/** @return callable(TWell|null $content, string $coordinatesString): bool */ | ||
public function matchColumn(int $column): callable | ||
{ | ||
return function ($content, string $coordinatesString) use ($column): bool { | ||
$coordinates = Coordinates::fromString($coordinatesString, $this->coordinateSystem); | ||
|
||
return $coordinates->column === $column; | ||
}; | ||
} | ||
|
||
/** | ||
* @deprecated use toWellWithCoordinatesMapper | ||
* | ||
* @return callable(TWell $content, string $coordinatesString): WellWithCoordinates<TWell, TCoordinateSystem> | ||
*/ | ||
public function toWellWithCoordinateMapper(): callable | ||
{ | ||
return $this->toWellWithCoordinatesMapper(); | ||
} | ||
|
||
/** @return callable(TWell $content, string $coordinatesString): WellWithCoordinates<TWell, TCoordinateSystem> */ | ||
public function toWellWithCoordinatesMapper(): callable | ||
{ | ||
return fn ($content, string $coordinatesString): WellWithCoordinates => new WellWithCoordinates( | ||
$content, | ||
Coordinates::fromString($coordinatesString, $this->coordinateSystem) | ||
); | ||
} | ||
|
||
/** | ||
* Are all filled wells placed in a single connected block without gaps between them? | ||
* | ||
* Returns `false` if all wells are empty. | ||
*/ | ||
public function isConsecutive(FlowDirection $flowDirection): bool | ||
{ | ||
$positions = $this->filledWells() | ||
->map( | ||
/** @param TWell $content */ | ||
fn ($content, string $coordinatesString): int => Coordinates::fromString($coordinatesString, $this->coordinateSystem)->position($flowDirection) | ||
); | ||
|
||
return ($positions->max() - $positions->min() + 1) === $positions->count(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace MLL\Utils\Microplate; | ||
|
||
use Illuminate\Support\Collection; | ||
|
||
/** | ||
* @template TSectionWell | ||
*/ | ||
abstract class AbstractSection | ||
{ | ||
/** @var SectionedMicroplate<TSectionWell, CoordinateSystem, static> */ | ||
public SectionedMicroplate $sectionedMicroplate; | ||
|
||
/** @var Collection<int, TSectionWell|null> */ | ||
public Collection $sectionItems; | ||
|
||
/** @param SectionedMicroplate<TSectionWell, CoordinateSystem, static> $sectionedMicroplate */ | ||
public function __construct(SectionedMicroplate $sectionedMicroplate) | ||
{ | ||
$this->sectionedMicroplate = $sectionedMicroplate; | ||
$this->sectionItems = new Collection(); | ||
} | ||
|
||
/** @param TSectionWell $content */ | ||
abstract public function addWell($content): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace MLL\Utils\Microplate\Casts; | ||
|
||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; | ||
use Illuminate\Database\Eloquent\Model; | ||
use MLL\Utils\Microplate\Coordinates; | ||
use MLL\Utils\Microplate\CoordinateSystem96Well; | ||
|
||
/** | ||
* @implements CastsAttributes<Coordinates<CoordinateSystem96Well>, Coordinates<CoordinateSystem96Well>> | ||
*/ | ||
final class Coordinates96Well implements CastsAttributes | ||
{ | ||
/** | ||
* @param Model $model | ||
* @param string $key | ||
* @param array<array-key, mixed> $attributes | ||
* | ||
* @return Coordinates<CoordinateSystem96Well> | ||
*/ | ||
public function get($model, $key, $value, $attributes): Coordinates | ||
{ | ||
assert(is_string($value)); | ||
|
||
return Coordinates::fromString($value, new CoordinateSystem96Well()); | ||
} | ||
|
||
/** | ||
* @param Model $model | ||
* @param string $key | ||
* @param array<array-key, mixed> $attributes | ||
*/ | ||
public function set($model, $key, $value, $attributes): string | ||
{ | ||
assert($value instanceof Coordinates); | ||
|
||
return $value->toString(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace MLL\Utils\Microplate; | ||
|
||
use Illuminate\Support\Arr; | ||
|
||
abstract class CoordinateSystem | ||
{ | ||
/** @return list<string> */ | ||
abstract public function rows(): array; | ||
|
||
/** @return list<int> */ | ||
abstract public function columns(): array; | ||
|
||
/** | ||
* List of columns, 0-padded to all have the same length. | ||
* | ||
* @return list<string> | ||
*/ | ||
public function paddedColumns(): array | ||
{ | ||
$paddedColumns = []; | ||
foreach ($this->columns() as $column) { | ||
$paddedColumns[] = $this->padColumn($column); | ||
} | ||
|
||
return $paddedColumns; | ||
} | ||
|
||
/** 0-pad column to be as long as the longest column in the coordinate system. */ | ||
public function padColumn(int $column): string | ||
{ | ||
$maxColumnLength = strlen((string) $this->columnsCount()); | ||
|
||
return str_pad((string) $column, $maxColumnLength, '0', STR_PAD_LEFT); | ||
} | ||
|
||
public function rowForRowFlowPosition(int $position): string | ||
{ | ||
return $this->rows()[floor(($position - 1) / $this->columnsCount())]; | ||
} | ||
|
||
public function rowForColumnFlowPosition(int $position): string | ||
{ | ||
return $this->rows()[($position - 1) % $this->rowsCount()]; | ||
} | ||
|
||
public function columnForRowFlowPosition(int $position): int | ||
{ | ||
return $this->columns()[($position - 1) % $this->columnsCount()]; | ||
} | ||
|
||
public function columnForColumnFlowPosition(int $position): int | ||
{ | ||
return $this->columns()[floor(($position - 1) / $this->rowsCount())]; | ||
} | ||
|
||
public function positionsCount(): int | ||
{ | ||
return $this->columnsCount() * $this->rowsCount(); | ||
} | ||
|
||
/** | ||
* Returns all possible coordinates of the system, ordered by column then row. | ||
* | ||
* e.g. A1, A2, B1, B2 | ||
* | ||
* @return iterable<int, Coordinates<$this>> | ||
*/ | ||
public function all(): iterable | ||
{ | ||
foreach ($this->columns() as $column) { | ||
foreach ($this->rows() as $row) { | ||
yield new Coordinates($row, $column, $this); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Returns the coordinates of the first row and first column. | ||
* | ||
* @return Coordinates<$this> | ||
*/ | ||
public function first(): Coordinates | ||
{ | ||
$firstRow = Arr::first($this->rows()); | ||
if (! is_string($firstRow)) { | ||
throw new \Exception('First row must be string.'); | ||
} | ||
|
||
$firstColumn = Arr::first($this->columns()); | ||
if (! is_int($firstColumn)) { | ||
throw new \Exception('First column must be string.'); | ||
} | ||
|
||
return new Coordinates($firstRow, $firstColumn, $this); | ||
} | ||
|
||
/** | ||
* Returns the coordinates of the last row and last column. | ||
* | ||
* @return Coordinates<$this> | ||
*/ | ||
public function last(): Coordinates | ||
{ | ||
$lastRow = Arr::last($this->rows()); | ||
if (! is_string($lastRow)) { | ||
throw new \Exception('Last row must be string.'); | ||
} | ||
|
||
$lastColumn = Arr::last($this->columns()); | ||
if (! is_int($lastColumn)) { | ||
throw new \Exception('Last column must be string.'); | ||
} | ||
|
||
return new Coordinates($lastRow, $lastColumn, $this); | ||
} | ||
|
||
public function rowsCount(): int | ||
{ | ||
return count($this->rows()); | ||
} | ||
|
||
public function columnsCount(): int | ||
{ | ||
return count($this->columns()); | ||
} | ||
} |
Oops, something went wrong.