Skip to content

Commit

Permalink
Proper Readme; Move to namespace PhilippR\Atk4\Cron
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippGrashoff committed Sep 7, 2023
1 parent 40dfd53 commit 11c0740
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 43 deletions.
34 changes: 23 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
# mtomforatk
[![codecov](https://codecov.io/gh/PhilippGrashoff/mtomforatk/branch/master/graph/badge.svg)](https://codecov.io/gh/PhilippGrashoff/mtomforatk)
An addition to atk4/data to easily manage Many To Many (MToM) Relations. The purpose
is to write as little code as possible for actual MToM operations.

An addition to [atk4/data](https://github.com/atk4/data) to easily manage Many To Many (MToM) Relations. The purpose is to write as little code as possible for actual MToM operations.

# Project Content
The project consists of two files:
* MToMModel: A base model for the intermediate class (like StudentToLesson). Working descendants can be coded with a few lines of code.
* MToMRelationForModelTrait: A Trait which is added to the models to be linked, (like Student and Lesson). With this trait, only a few more lines need to be added to make operations like `$lesson->addStudent(5);` work.
* **JunctionModel**: A base model for the junction class (like StudentToLesson). Working descendants can be coded with a few lines of code.
* **MToMTrait**: A Trait which is added to the models to be linked, (like Student and Lesson). With this trait, only a few more lines need to be added to make operations like `$lesson->addStudent(5);` work.

# How to use
## Installation
The easiest way to use this repository is to add it to your composer.json in the require section:
The easiest way to use this repository is to add it to your composer.json in the requirement section:
```json
{
"require": {
"philippgrashoff/cronforatk": "4.0.*"
"philippgrashoff/mtomforatk": "4.0.*"
}
}
```
## Sample code
As example, lets use Students and Lessons. A Student can have many Lessons, a Lesson can have many Students.
To map this MToM relationship, 3 classes are created. Demo models for this example can be found in tests\Testmodels:
* Student: A normal model which additionally uses ModelWithMToMTrait. 3 little helpers methods are implemented to make MToM handling easier: addLesson(), removeLesson() and hasLesson();
* Lesson: A normal model which additionally uses ModelWithMToMTrait. 3 little helpers methods are implemented to make MToM handling easier: addStudent(), removeStudent() and hasStudent();
* StudentToLesson: The intermediate model carrying the student_id and lesson_id for each MToM relation between Students and Lessons.
* Student: A normal model which uses MToMTrait. 3 little helpers methods are implemented to make MToM handling easier: addLesson(), removeLesson() and hasLesson();
* Lesson: A normal model which uses MToMTrait. 3 little helpers methods are implemented to make MToM handling easier: addStudent(), removeStudent() and hasStudent();
* StudentToLesson: The junction model carrying the student_id and lesson_id for each MToM relation between Students and Lessons.

After setting these classes up using this project, MToM operations can be done easily:
```php
Expand All @@ -42,7 +42,7 @@ $studentHarry->addLesson($lessonGeography);
$lessonBiology = (new Lesson($persistence))->createEntity();
$lessonBiology->set('name', 'Biology');
$lessonBiology->save();
//adding/removing can either be done by passong the other model or only it's ID. In this case, we just pass the ID
//adding/removing can either be done by passing the other model or only it's ID. In this case, we just pass the ID - that's what you typically get from UI
$studentHarry->addLesson($lessonBiology->getId());
//this created another StudentToLesson record with student_id = studentHarry's ID and lesson_id = lessonBiology's ID

Expand All @@ -53,4 +53,16 @@ $studentHarry->hasLesson($lessonGeography); //true;
$studentHarry->removeLesson($lessonGeography);
//this removed the StudentToLesson Record linking Harry to Geography.
$studentHarry->hasLesson($lessonGeography); //false
```

//Linda attends both courses. Let's add Linda to both courses. But this time we do it the other way around: We use Lesson's helper functions:
$studentLinda = (new Student($persistence))->createEntity();
$studentLinda->set('name', 'Linda');
$studentLinda->save();
$lessonGeography->addStudent($studentLinda);
$lessonBiology->addStudent($studentLinda);
```

The sample code from this readme can be found in the `docs` directory.

# Versioning
The version numbers of this repository correspond with the atk4\data versions. So 4.0.x is compatible with atk4\data 4.0.x and so on.
14 changes: 11 additions & 3 deletions docs/sample.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<?php declare(strict_types=1);

use Atk4\Data\Persistence\Sql;
use PhilippR\Atk4\MToM\Tests\Testmodels\Lesson;
use PhilippR\Atk4\MToM\Tests\Testmodels\Student;

$persistence = new \Atk4\Data\Persistence\Sql('sqlite::memory:');
$persistence = new Sql('sqlite::memory:');

$studentHarry = (new Student($persistence))->createEntity();
$studentHarry->set('name', 'Harry');
Expand All @@ -20,7 +21,7 @@
$lessonBiology = (new Lesson($persistence))->createEntity();
$lessonBiology->set('name', 'Biology');
$lessonBiology->save();
//adding/removing can either be done by passong the other model or only it's ID. In this case, we just pass the ID
//adding/removing can either be done by passing the other model or only it's ID. In this case, we just pass the ID - that's what you typically get from UI
$studentHarry->addLesson($lessonBiology->getId());
//this created another StudentToLesson record with student_id = studentHarry's ID and lesson_id = lessonBiology's ID

Expand All @@ -30,4 +31,11 @@
//harry is tired of Geography, lets remove him from this lesson:
$studentHarry->removeLesson($lessonGeography);
//this removed the StudentToLesson Record linking Harry to Geography.
$studentHarry->hasLesson($lessonGeography); //false
$studentHarry->hasLesson($lessonGeography); //false

//Linda attends both courses. Let's add Linda to both courses. But this time we do it the other way around: We use Lesson's helper functions:
$studentLinda = (new Student($persistence))->createEntity();
$studentLinda->set('name', 'Linda');
$studentLinda->save();
$lessonGeography->addStudent($studentLinda);
$lessonBiology->addStudent($studentLinda);
8 changes: 4 additions & 4 deletions src/IntermediateModel.php → src/JunctionModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use Atk4\Data\Model;


abstract class IntermediateModel extends Model
abstract class JunctionModel extends Model
{

/**
Expand Down Expand Up @@ -88,7 +88,7 @@ public function getReferencedEntity(string $className): Model


/**
* used by ModelWithMToMTrait to make records available in getReferencedEntity() without extra DB request
* used by MToMTrait to make records available in getReferencedEntity() without extra DB request
*
* @param Model $entity
* @return void
Expand All @@ -108,7 +108,7 @@ public function addReferencedEntity(Model $entity): void


/**
* used by ModelWithMToMTrait to get the correct field name that corresponds to one of the linked Models
* used by MToMTrait to get the correct field name that corresponds to one of the linked Models
*
* @param Model $model
* @return string
Expand Down Expand Up @@ -142,7 +142,7 @@ public function addConditionForModel(Model $entity): void


/**
* We will have 2 Model classes defined which the MToMModel will connect. This function returns the class name of
* We have 2 Model classes defined which the JunctionModel will connect. This function returns the class name of
* the other class if one is passed
*
* @param Model $model
Expand Down
30 changes: 15 additions & 15 deletions src/MToMTait.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@ trait MToMTait
* Create a new MToM relation, e.g. a new StudentToLesson record. Called from either Student or Lesson class.
* First checks if record does exist already, and only then adds new relation.
*
* @param IntermediateModel $mToMModel
* @param JunctionModel $mToMModel
* @param int|Model $otherEntity //if int, then it's only an ID
* @param array<string,mixed> $additionalFields
* @return IntermediateModel
* @return JunctionModel
* @throws Exception
* @throws \Atk4\Core\Exception
*/
public function addMToMRelation(
IntermediateModel $mToMModel,
JunctionModel $mToMModel,
int|Model $otherEntity,
array $additionalFields = []
): IntermediateModel {
): JunctionModel {
//$this needs to be loaded to get ID
$this->assertIsLoaded();
$otherEntity = $this->getOtherEntity($otherEntity, $mToMModel);
Expand Down Expand Up @@ -60,12 +60,12 @@ public function addMToMRelation(
* method used to remove a MToMModel record like StudentToLesson. Either used from Student or Lesson class.
* GuestToGroup etc.
*
* @param IntermediateModel $mToMModel
* @param JunctionModel $mToMModel
* @param int|Model $otherEntity //if int, then it's only an ID
* @return IntermediateModel
* @return JunctionModel
* @throws Exception
*/
public function removeMToMRelation(IntermediateModel $mToMModel, int|Model $otherEntity): IntermediateModel
public function removeMToMRelation(JunctionModel $mToMModel, int|Model $otherEntity): JunctionModel
{
//$this needs to be loaded to get ID
$this->assertIsLoaded();
Expand All @@ -85,12 +85,12 @@ public function removeMToMRelation(IntermediateModel $mToMModel, int|Model $othe
* checks if a MtoM reference to the given entity exists or not, e.g. if a StudentToLesson record exists for a
* specific student and lesson
*
* @param IntermediateModel $mToMModel
* @param JunctionModel $mToMModel
* @param int|Model $otherEntity //if int, then it's only an ID
* @return bool
* @throws Exception
*/
public function hasMToMRelation(IntermediateModel $mToMModel, int|Model $otherEntity): bool
public function hasMToMRelation(JunctionModel $mToMModel, int|Model $otherEntity): bool
{
$this->assertIsLoaded();
$otherEntity = $this->getOtherEntity($otherEntity, $mToMModel);
Expand All @@ -103,12 +103,12 @@ public function hasMToMRelation(IntermediateModel $mToMModel, int|Model $otherEn
}

/**
* 1) adds HasMany Reference to intermediate model.
* 2) adds after delete hook which deletes any intermediate model linked to the deleted "main" model.
* This way, no outdated intermediate models exist.
* 1) adds HasMany Reference linking the junction model.
* 2) adds after delete hook which deletes any junctions models linked to the deleted "main" model.
* This way, no outdated junction models exist.
* Returns HasMany reference for further modifying reference if needed.
*
* @param class-string<IntermediateModel> $mtomClassName
* @param class-string<JunctionModel> $mtomClassName
* @param string $referenceName
* @param array<string,mixed> $referenceDefaults
* @param array<string,mixed> $mtomClassDefaults
Expand Down Expand Up @@ -157,11 +157,11 @@ function ($model) use ($referenceName): void {
* Check other model is loaded so id can be gotten.
*
* @param int|Model $otherEntity //if int, then it's only an ID
* @param IntermediateModel $mToMModel
* @param JunctionModel $mToMModel
* @return Model
* @throws Exception
*/
protected function getOtherEntity(int|Model $otherEntity, IntermediateModel $mToMModel): Model
protected function getOtherEntity(int|Model $otherEntity, JunctionModel $mToMModel): Model
{
$otherModelClass = $mToMModel->getOtherModelClass($this);
if (is_object($otherEntity)) {
Expand Down
12 changes: 6 additions & 6 deletions tests/IntermediateModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
use Atk4\Data\Exception;
use Atk4\Data\Model;
use atkextendedtestcase\TestCase;
use PhilippR\Atk4\MToM\IntermediateModel;
use PhilippR\Atk4\MToM\JunctionModel;
use PhilippR\Atk4\MToM\Tests\Testmodels\Lesson;
use PhilippR\Atk4\MToM\Tests\Testmodels\Student;
use PhilippR\Atk4\MToM\Tests\Testmodels\StudentToLesson;
use PhilippR\Atk4\MToM\Tests\Testmodels\Teacher;
use ReflectionClass;


class MToMModelTest extends TestCase
class IntermediateModelTest extends TestCase
{
protected array $sqlitePersistenceModels = [
Student::class,
Expand All @@ -34,7 +34,7 @@ public function testInit(): void
public function testExceptionMoreThanTwoElementsInFieldNamesForReferencedClasses(): void
{
$persistence = $this->getSqliteTestPersistence();
$someClassWith3Elements = new class() extends IntermediateModel {
$someClassWith3Elements = new class() extends JunctionModel {
protected array $relationFieldNames = [
'field1' => 'Blabla',
'field2' => 'DaDa',
Expand All @@ -48,7 +48,7 @@ public function testExceptionMoreThanTwoElementsInFieldNamesForReferencedClasses
public function testExceptionLessThanTwoElementsInFieldNamesForReferencedClasses(): void
{
$persistence = $this->getSqliteTestPersistence();
$someClassWith1Element = new class() extends IntermediateModel {
$someClassWith1Element = new class() extends JunctionModel {
protected array $relationFieldNames = [
'field1' => 'Blabla'
];
Expand All @@ -60,7 +60,7 @@ public function testExceptionLessThanTwoElementsInFieldNamesForReferencedClasses
public function testExceptionInvalidClassInFieldNamesForReferencedClasses(): void
{
$persistence = $this->getSqliteTestPersistence();
$someClassWithInvalidClassDefinition = new class() extends IntermediateModel {
$someClassWithInvalidClassDefinition = new class() extends JunctionModel {
protected array $relationFieldNames = [
'field1' => Student::class,
'field2' => 'SomeNonExistantModel'
Expand Down Expand Up @@ -203,7 +203,7 @@ public function testAddReferencedEntityExceptionInvalidModelClassGiven(): void
$teacher = (new Teacher($persistence))->createEntity();
$teacher->save();
self::expectExceptionMessage(
'This PhilippR\Atk4\MToM\MToMModel does not have a reference to mtomforatk\tests\Testmodels\Teacher'
'This PhilippR\Atk4\MToM\JunctionModel does not have a reference to PhilippR\Atk4\MToM\Tests\Testmodels\Teacher'
);
$studentToLesson->addReferencedEntity($teacher);
}
Expand Down
15 changes: 15 additions & 0 deletions tests/Testmodels/Lesson.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,19 @@ protected function init(): void
$this->addMToMReferenceAndDeleteHook(StudentToLesson::class);
$this->addMToMReferenceAndDeleteHook(TeacherToLesson::class);
}

public function addStudent($student, array $additionalFields = []): StudentToLesson
{
return $this->addMToMRelation(new StudentToLesson($this->getPersistence()), $student, $additionalFields);
}

public function removeStudent($student): StudentToLesson
{
return $this->removeMToMRelation(new StudentToLesson($this->getPersistence()), $student);
}

public function hasStudent($student): bool
{
return $this->hasMToMRelation(new StudentToLesson($this->getPersistence()), $student);
}
}
4 changes: 2 additions & 2 deletions tests/Testmodels/StudentToLesson.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

namespace PhilippR\Atk4\MToM\Tests\Testmodels;

use PhilippR\Atk4\MToM\IntermediateModel;
use PhilippR\Atk4\MToM\JunctionModel;

class StudentToLesson extends IntermediateModel
class StudentToLesson extends JunctionModel
{
public $table = 'student_to_lesson';

Expand Down
4 changes: 2 additions & 2 deletions tests/Testmodels/TeacherToLesson.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

namespace PhilippR\Atk4\MToM\Tests\Testmodels;

use PhilippR\Atk4\MToM\IntermediateModel;
use PhilippR\Atk4\MToM\JunctionModel;


class TeacherToLesson extends IntermediateModel
class TeacherToLesson extends JunctionModel
{
public $table = 'teacher_to_lesson';

Expand Down

0 comments on commit 11c0740

Please sign in to comment.