Skip to content

Commit

Permalink
Fixed Tests, sensible Renamings done, ready for release :)
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippGrashoff committed Aug 12, 2023
1 parent a931639 commit 260432d
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 133 deletions.
72 changes: 38 additions & 34 deletions src/MToMModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@ abstract class MToMModel extends Model
{

/**
* @var array<string,class-string|null> $fieldNamesForReferencedClasses
* @var array<string,class-string|null> $fieldNamesForReferencedEntities
* with 2 keys and 2 values. Set these four strings child classes.
* Will be used to create hasOne Reference Fields in init()
* e.g. [
* 'student_id' => Student::class,
* 'lesson_id' => Lesson::class
* ]
*/
protected array $fieldNamesForReferencedClasses = [];
protected array $fieldNamesForReferencedEntities = [];

/**
* @var array<class-string,Model|null> $referenceObjects containing instances of the two linked models. Useful for re-using them and saving DB requests
* @var array<class-string,Model|null> $referencedEntities
* containing instances of the two linked models. Useful for re-using them and saving DB requests
*/
protected array $referenceObjects = [];
protected array $referencedEntities = [];


/**
Expand All @@ -37,70 +38,72 @@ protected function init(): void
{
parent::init();
//make sure 2 classes to link are defined
if (count($this->fieldNamesForReferencedClasses) !== 2) {
if (count($this->fieldNamesForReferencedEntities) !== 2) {
throw new Exception(
'2 Fields and corresponding classes need to be defined in fieldNamesForReferencedClasses array'
);
}
if (
!class_exists(reset($this->fieldNamesForReferencedClasses))
|| !class_exists(end($this->fieldNamesForReferencedClasses))
!class_exists(reset($this->fieldNamesForReferencedEntities))
|| !class_exists(end($this->fieldNamesForReferencedEntities))
) {
throw new Exception('Non existent Class defined in fieldNamesForReferencedClasses array');
}

foreach ($this->fieldNamesForReferencedClasses as $fieldName => $className) {
foreach ($this->fieldNamesForReferencedEntities as $fieldName => $className) {
/** @var class-string $className */
$this->hasOne($fieldName, ['model' => [$className]]);
$this->referenceObjects[$className] = null;
$this->hasOne($fieldName, ['model' => [$className], 'required' => true]);
$this->referencedEntities[$className] = null;
}
}


/**
* Shortcut to get an entity from each of the linked classes. e.g.
* $studentToLesson = $student->addLesson(4); //add Lesson by ID, no lesson object yet
* $studentToLesson = $student->addLesson(4); //add Lesson by ID, no lesson entity yet
* $lesson = $studentToLesson->getReferenceEntity(Lesson::class); //will return Lesson record with ID 4
*
* @param class-string<Model> $className
* @return Model|null
* @throws Exception
*/
public function getReferenceEntity(string $className): ?Model
public function getReferencedEntity(string $className): Model
{
if (!array_key_exists($className, $this->referenceObjects)) {
$this->assertIsEntity();
if (!array_key_exists($className, $this->referencedEntities)) {
throw new Exception('Invalid className passed in ' . __FUNCTION__);
}

//load if necessary
if (!$this->referenceObjects[$className] instanceof $className) {
$this->referenceObjects[$className] = new $className($this->getPersistence());
if ($this->referencedEntities[$className] === null) {
$model = new $className($this->getPersistence());
//will throw exception if record isn't found
$this->referenceObjects[$className]->load(
$this->get(array_search($className, $this->fieldNamesForReferencedClasses))
$this->referencedEntities[$className] = $model->load(
$this->get(array_search($className, $this->fieldNamesForReferencedEntities))
);
}

return $this->referenceObjects[$className];
return $this->referencedEntities[$className];
}


/**
* used by ModelWithMToMTrait to make records available in getObject() without extra DB request
* used by ModelWithMToMTrait to make records available in getReferencedEntity() without extra DB request
*
* @param Model $model
* @param Model $entity
* @return void
* @throws Exception
*/

public function addReferenceEntity(Model $model): void
public function addReferencedEntity(Model $entity): void
{
$modelClass = get_class($model);
if (!array_key_exists($modelClass, $this->referenceObjects)) {
throw new Exception('This class does not have a reference to ' . $modelClass);
$entity->assertIsEntity();
$modelClass = get_class($entity);
if (!array_key_exists($modelClass, $this->referencedEntities)) {
throw new Exception('This ' . __CLASS__ . ' does not have a reference to ' . $modelClass);
}

$this->referenceObjects[$modelClass] = $model;
$this->referencedEntities[$modelClass] = $entity;
}


Expand All @@ -113,10 +116,10 @@ public function addReferenceEntity(Model $model): void
*/
public function getFieldNameForModel(Model $model): string
{
$fieldName = array_search(get_class($model), $this->fieldNamesForReferencedClasses);
$fieldName = array_search(get_class($model), $this->fieldNamesForReferencedEntities);
if (!$fieldName) {
throw new Exception(
'No field name defined in $fieldNamesForReferencedClasses for Class ' . get_class($model)
'No field name defined in $fieldNamesForReferencedEntities for Class ' . get_class($model)
);
}

Expand All @@ -127,13 +130,14 @@ public function getFieldNameForModel(Model $model): string
/**
* results ín e.g. $this->addCondition('student_id', 5);
*
* @param Model $model
* @param Model $entity
* @return void
* @throws Exception
*/
public function addConditionForModel(Model $model): void
public function addConditionForModel(Model $entity): void
{
$this->addCondition($this->getFieldNameForModel($model), $model->getId());
$entity->assertIsEntity();
$this->addCondition($this->getFieldNameForModel($entity), $entity->getId());
}


Expand All @@ -148,14 +152,14 @@ public function addConditionForModel(Model $model): void
public function getOtherModelClass(Model $model): string
{
$modelClass = get_class($model);
if (!in_array($modelClass, $this->fieldNamesForReferencedClasses)) {
if (!in_array($modelClass, $this->fieldNamesForReferencedEntities)) {
throw new Exception('Class ' . $modelClass . 'not found in fieldNamesForReferencedClasses');
}

//as array has 2 elements, return second if passed class is the first, else otherwise
if (reset($this->fieldNamesForReferencedClasses) === $modelClass) {
return end($this->fieldNamesForReferencedClasses);
if (reset($this->fieldNamesForReferencedEntities) === $modelClass) {
return end($this->fieldNamesForReferencedEntities);
}
return reset($this->fieldNamesForReferencedClasses);
return reset($this->fieldNamesForReferencedEntities);
}
}
54 changes: 26 additions & 28 deletions src/ModelWithMToMTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,24 @@ public function addMToMRelation(
//check if reference already exists, if so update existing record only
$mToMModel->addConditionForModel($this);
$mToMModel->addConditionForModel($otherModel);
$mToMModel->tryLoadAny();
//no reload necessary after insert
$mToMModel->reloadAfterSave = false;
$mToMEntity = $mToMModel->tryLoadAny() ?? $mToMModel->createEntity();

//set values
$mToMModel->set($mToMModel->getFieldNameForModel($this), $this->getId());
$mToMModel->set($mToMModel->getFieldNameForModel($otherModel), $otherModel->getId());
$mToMEntity->set($mToMEntity->getFieldNameForModel($this), $this->getId());
$mToMEntity->set($mToMEntity->getFieldNameForModel($otherModel), $otherModel->getId());

//set additional field values
foreach ($additionalFields as $fieldName => $value) {
$mToMModel->set($fieldName, $value);
$mToMEntity->set($fieldName, $value);
}

//no reload necessary after insert
$mToMModel->reloadAfterSave = false;
//if that record already exists mysql will throw an error if unique index is set, catch here
$mToMModel->save();
$mToMModel->addReferenceEntity($this);
$mToMModel->addReferenceEntity($otherModel);
$mToMEntity->save();
$mToMEntity->addReferencedEntity($this);
$mToMEntity->addReferencedEntity($otherModel);

return $mToMModel;
return $mToMEntity;
}


Expand All @@ -75,15 +74,15 @@ public function removeMToMRelation(MToMModel $mToMModel, string|int|Model $other
$mToMModel->addConditionForModel($this);
$mToMModel->addConditionForModel($otherModel);
//loadAny as it will throw exception when record is not found
$mToMModel->loadAny();
$mToMModel = $mToMModel->loadAny();
$mToMModel->delete();

return $mToMModel;
}


/**
* checks if a MtoM reference to the given object exists or not, e.g. if a StudentToLesson record exists for a
* 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 MToMModel $mToMModel
Expand All @@ -98,9 +97,9 @@ public function hasMToMRelation(MToMModel $mToMModel, string|int|Model $otherMod

$mToMModel->addConditionForModel($this);
$mToMModel->addConditionForModel($otherModel);
$mToMModel->tryLoadAny();
$mToMEntity = $mToMModel->tryLoadAny();

return $mToMModel->isLoaded();
return $mToMEntity !== null;
}

/**
Expand Down Expand Up @@ -157,34 +156,33 @@ function ($model) use ($referenceName): void {
* Make sure passed model is of the correct class.
* Check other model is loaded so id can be gotten.
*
* @param string|int|Model $otherModel //if string or int, then it's only an ID
* @param string|int|Model $otherEntity //if string or int, then it's only an ID
* @param MToMModel $mToMModel
* @return Model
* @throws Exception
*/
protected function getOtherEntity(string|int|Model $otherModel, MToMModel $mToMModel): Model
protected function getOtherEntity(string|int|Model $otherEntity, MToMModel $mToMModel): Model
{
/** @var class-string<Model> $otherModelClass */
$otherModelClass = $mToMModel->getOtherModelClass($this);
if (is_object($otherModel)) {
if (is_object($otherEntity)) {
//only check if it's a model of the correct class; also check if accidentally $this was passed
if (get_class($otherModel) !== $otherModelClass) {
if (get_class($otherEntity) !== $otherModelClass) {
throw new Exception(
'Object of wrong class was passed: ' . $mToMModel->getOtherModelClass($this)
. 'expected, ' . get_class($otherModel) . ' passed.'
. 'expected, ' . get_class($otherEntity) . ' passed.'
);
}
} else {
$id = $otherModel;
$otherModel = new $otherModelClass($this->getPersistence());
$otherModel->tryLoad($id);
$id = $otherEntity;
$otherEntity = new $otherModelClass($this->getPersistence());
$otherEntity = $otherEntity->tryLoad($id);
}

//make sure object is loaded
if (!$otherModel->isLoaded()) {
throw new Exception('Object could not be loaded in ' . __FUNCTION__);
//make sure entity is loaded
if (!$otherEntity || !$otherEntity->isLoaded()) {
throw new Exception('otherEntity could not be loaded in ' . __FUNCTION__);
}

return $otherModel;
return $otherEntity;
}
}
Loading

0 comments on commit 260432d

Please sign in to comment.