2
\$\begingroup\$

An Object-relational mapper built using php.

This was done for educational purposes, and curious to know what kind of improvement has to be done to use in production.

Please refer to the complete source code in the Github repository - (https://github.com/maleeshagimshan98/infinite-simple-orm)

Below, I've described the internal workings...

  • Entity Manager Class

    • This is the main instance we are interacting with.

    • Use it to retrieve and Create/Update data

    • It uses Entity objects and their attributes to build the query and execute the query.

    • Handles associations with other Entities using entity attributes ( $Entity->associations->get())

  • Entity Class

    • Represent an entity in the database.

    • Has attributes of entity and column mappings to attributes for respective table

    • Has associations info that Entity has.

  • Entity class structure is here,

    • Entity class
      • attributes are managed using AttributeContainer class.
      • associations are managed using AssociatedEntityContainer class.
      • attribs_map - attribute to column mapping, managed by AttributeMapContainer class

AttributeContainer Class

<?php
/**
 * © Maleesha Gimshan - 2021 - github.com/maleeshagimshan98
 *
 * Entity's Attribute Container Class
 */

 namespace Infinite\DataMapper\Entity;
 /**
  * Attribute container
  */
 class AttributeContainer
 {

   /**
    * entity attributes
    *
    * @var array
    */
   protected $attribs = [];

   /**
    * entity property names
    *
    * @var array
    */
   protected $props = [];

   /**
    * constructor
    */
     public function __construct ()
     {

     }

    /**
    * get a single attribute
    *
    * @param string $attrib
    * @return string attribute
    * @throws \Exception
     */
    protected function attribute ($attrib = null) 
    {
      if (empty($attrib)) {
         return $this->attribs;
      }

      if (!isset($this->attribs[$attrib])) {
         throw new \Exception("Undefined_Attribute");
      }
      return $this->attribs[$attrib];
    }

    /**
     * get entity's attributes
     *
     * @param mixed $attribute
     * @return string|array
     * @throws \Exception
     */
    public function get ($attribute = null)
    {
       try
       {
         if (is_array($attribute))
         {
            $arr = [];
            foreach ($attribute as $key) {                         
               $arr[] = $this->attribute($key);
            }
            return $arr;
         }
         return $this->attribute($attribute);
       }
      catch (\Exception $e)
      {
         if ($e->getMessage() == "Undefined_Attribute" )
         {
            return $this->attribute();
         }
      }       
   }

   /**
    * get entity's properties
    * properties = $this->attribs[$name] - this $name is a property
    *
    * @return void
    */
   public function getAttribKeys ($prop = null)
   {
      if (isset($prop))
      {
         if (!isset($this->props[$prop]))
         {
            throw new \Exception("Undefined_Prop");
         }
         return $this->props[$prop];
      }
      return $this->props;
   }

   /**
    * set entity's attribute
    *
    * @param string $name
    * @param array|object $attrib
    * @return void
    */
    public function set ($name,$attrib)
    {
       $this->props[] = $name;
       $this->attribs[$name] = $attrib;         
    }
 }

 ?>

AttributeMapContainer Class

<?php
/**
 * © Maleesha Gimshan - 2021 - github.com/maleeshagimshan98
 *
 * Entity's Attribute Container Class
 */

 namespace Infinite\DataMapper\Entity;
 /**
  * Attribute Map Container
  */
 class AttributeMapContainer
 {

   /**
    * entity attributes
    *
    * @var array
    */
   protected $attrib_map = [];

   /**
    * constructor
    */
     public function __construct ()
     {
        
     }
    /**
    * get a single attribute_map element
    *
    * @param string $attrib attrib_map element name
    * @return string attribute_map element
    * @throws \Exception
     */
    protected function map ($attrib = null) 
    {      
      if (!isset($this->attrib_map[$attrib])) {
         throw new \Exception("Undefined_Attribute");
      }
      return $this->attrib_map[$attrib];
    }

    /**
     * get entity's attributes
     *
     * @param mixed $attribute
     * @return string|array
     * @throws \Exception
     */
    public function get ($attribute = null)
    {
       if (empty($attribute))
         {
            $arr = [];
            foreach ($this->attrib_map as $key) {                         
               $arr[] = $key;
            }
            return $arr;
         } 
         if (is_array($attribute))
         {
            $arr = [];
            foreach ($attribute as $key) {                         
               $arr[] = $this->map($key);
            }
            return $arr;
         }
         return $this->map($attribute); /** if $attribute === String */            
   }

    
    public function set ($name,$attrib)
    {
       $this->attrib_map[$name] = $attrib;         
    }
 }

 ?>

AssociatedEntityContainer Class

<?php
/**
 * © Maleesha Gimshan - 2021 - github.com/maleeshagimshan98
 *
 * Associated Entity Container Class
 */

 namespace Infinite\DataMapper\Entity;
 /**
  * Associated entity container
  */
 class AssociatedEntityContainer 
 {

    /**
     * entity association data
     *
     * @var array
     */
    protected $associations = [];

    protected $associationKeys = [];

    /**
     * currently associated entities on this entity
     *
     * @var array
     */
    protected $currentAssociated = [];

    /**
     * constructor
     */
     public function __construct ()
     {

     }

     /**
      * get an association
      *
      * @param string $association
      * @return string
      * @throws \Exception
      */
     public function get ($association = null)
     {
        if (empty($association))
        {
            return $this->associations;
        }     
         if (!isset($this->associations[$association]))
         {
            throw new \Exception("Undefined_Association");
         }  
         return $this->associations[$association];
     }

     /**
      * get associations key names
      *
      * @return array
      */
     public function getAssociationKeys () 
     {
        return $this->associationKeys;
     }

     /**
      * set an association
      *
      * @param object $data
      * @return void
      * @throws \Exception
      */
     public function set ($data)
     {        
        foreach ($data as $key => $value)
        {
           $this->associationKeys[] = $key;            
            if (empty($value->target))
            {
               throw new \Exception("Target_Entity_Undefined");
               return; //IMPORTANT CHECK WHAT HAPPENS IN CASE OF ERROR
            }            
            $this->associations[$key] = $value;
        }           
    }
 }

 ?>

Entity Class

<?php
/**
 * © Maleesha Gimshan - 2021 - github.com/maleeshagimshan98
 *
 * base class for defining entities
 * in the database
 */

 namespace Infinite\DataMapper;

 use Infinite\DataMapper\Entity\AttributeContainer;
 use Infinite\DataMapper\Entity\AttributeMapContainer;
 use Infinite\DataMapper\Entity\AssociatedEntityContainer;
 use Infinite\DataMapper\Entity\EntityResult;

 /**
  * Entity class
  * represents an entity
  */
 class Entity {
    protected $qb;

    /**
     * entity name
     *
     * @var string
     */
    public $entityName;

    /**
     * entity's attributes
     *
     * @var Infinite\DataMapper\Entity\AttributeContainer
     */
    public $attribs;

    /**
     * attributes mapped to column names - table_name.column_name
     *
     * @var array
     */
    public $attribs_map;

    /**
     * entity's property names
     *
     * @var Infinite\DataMapper\Entity\EntityResult object
     */
    protected $entityResult;

    /**
     * entity table's name
     *
     * @var string
     */
    protected $entity_table = "";

    /**
     * entity associations with other entities
     *
     * @var Infinite\DataMapper\Entity\AssociatedEntityContainer
     */
    public $associations;
    /**
     * entity's data limit - for pagination
     *
     * @var integer
     */
    protected $limit = 10;

    /**
     * constructor
     * 
     * @param string $name database table name of the entity    
     * @param array|object $attribs
     * @return void
     * @throws \Exceptions
     */
    public function __construct ($attribs,$name)
    {
       if (!$attribs) {
          throw new \Exception("Invalid_Entity_Definition");
      }
       $this->attribs = new AttributeContainer();
       $this->attribs_map = new AttributeMapContainer($name);
       $this->associations = new AssociatedEntityContainer();
       $this->entity_table = $name;
       $this->init($attribs,$name);
    }

    /**
     * get entity table
     *
     * @return string
     */
    public function table () : string
    {
      return $this->entityTable;
    }

    /**
     * get entity's name
     *
     * @return string
     */
    public function name () 
    {
       return $this->entityName;
    }

   /**
    * get primary key
    *
    * @return string|array
    */
   public function primary ()
   {
      return $this->primary;
   }

   /**
    * set primary key
    *
    * @param object $attrib
    * @return void
    */
   public function setPrimaryKey ($attrib,$name)
   {
      if (isset($attrib->primary) && $attrib->primary)
      {
         $this->primary[] = $attrib->name ?? $name ;
      }
   }

//....  

   
    /**
     * map entity attributes from configuration
     *
     * @param object $attribs entity attributes
     * @param string $name entity table name
     * @return Infinite\DataMapper\Entity\AttributeContainer attribute container
     */
    protected function init ($attribs,$name) : AttributeContainer
    { /* TESTING */   //echo \json_encode($attribs);
       $keys = [];
       foreach ($attribs as $key => $value) {
         if ($this->entity_table == "")
         {
            $this->entity_table = $name;
         }
          
          $this->setAttribute($key,$value);
          $this->setPrimaryKey($key,$value);
          if ($key === "_assoc") {
             $this->associations->set($value);
          }
       }
       $this->initEntityResult();       
       return $this->attribs;
      }
      
      /**
       * initialize EntityResult object
       *
       * @return void
       */
      protected function initEntityResult ()
      {  
         $keys = $this->attribs->getAttribKeys();
         $keys = array_merge($keys,$this->associations->getAssociationKeys());
         $this->entityResult = new EntityResult($keys,$this->entityName);         
      }

      /**
       * get entity result object
       *
       * @return Infinite\DataMapper\Entity\EntityResult object
       */
      public function getEntityResult ()
      {
         return $this->entityResult;
      }

    /**
     * set entity's attributes
     *
     * @param string $name
     * @param string|object $attrib
     * @return void
     */
    protected function setAttribute ($name,$attrib)
    {//echo \json_encode($attrib);        
       $attributeName = $this->parseAttributeName($name,$attrib);
       if ($attributeName)
       { 
         //setting the attribute mapping to columns
         $this->attribs_map->set($name,$attributeName);
         $this->attribs->set($name,$attrib);
       }
    }

    /**
     * sets the attribute's name with respect of configuration options
     *
     * @param string $attribName attribute name entity
     * @param string|object $attrib attribute options (if any), or (similar as $attribName)
     * @return string
     */
    protected function parseAttributeName ($attribName,$attrib)
    {      
       if (\is_object($attrib))
       {
          if (!empty($attrib->name))
          {
             $attribute = $attrib->name;
          }
          elseif (!empty($attrib->primary))
          { //attribs with primary key, but no name comes here
             $attribute = $attribName;
          }
          else
          {
             return false; //TODO - CHECK -- associations comes here
          }
       }
       elseif (is_string($attrib))
       {
          $attribute =  $attrib;
       }
       return $attribute;       
    }

    /**
     * format column names with table name
     *
     * @param string|array $columns
     * @return mixed
     */
    protected function withTable ($columns)
    {
       if (is_string($columns))
       {
          return $this->entityTable.".".$columns;
       }

       if (is_array($columns))
       {
          $formattedColumns = [];
          foreach ($columns as $key)
          {
             $formattedColumns[] = $this->entityTable.".".$key;
          }
          return $formattedColumns;
       }
    }

    /**
     * get entity attributes' column names (any or all)
     *     
     * @param string|array $attributeName
     * @return string|array
     */
    public function mapAttribsToColumn ($attributeName = null)
    {
       if (\is_null($attributeName))
       {
          return $this->withTable($this->attribs_map->get($attributeName));
       }      
       $attribs = $this->attribs->get($attributeName);
       if (is_array($attribs))
       {
         $columns = [];
         foreach ($attribs as $key => $val)
         {
            $columns[] = $this->withTable($this->attribs_map->get($val));
         }
         return $columns;
       }
       return $this->withTable($this->attribs_map->get($attributeName));
    }

    /**
     * hydrate entity with the data fetched from database
     *
     * @param array|object $data  fetched data from database
     * @return self
     */
    public function hydrate ($data)
    { 
       $data = (object) $data;
       $arr = [];
       $attribs = $this->attribs->get();
       foreach ($attribs as $key => $value)
       { 
          $attributeName = $this->parseAttributeName($key,$value);
          $arr[$key] = $data->$attributeName;          
       }
       return  new EntityResult($arr,$this->entityName); //always return a new instance
    }
    
    /**
     * process entity result object for insertion, based on entity's atrributes
     * (only entity owned attributes)
     *
     * @param [type] $data
     * @return void
     */
    public function insertProps ($data)
    {
       $arr = [];
       $attribs = $this->attribs->getAttribKeys();
       foreach ($attribs as $key)
       {
          $arr[] = $data[$key];
       }
       return $arr;
    }
    
 }
?>

Entity Manager

  • Entity Manager class structure
    • EntityContainer class - containing all entities.
    • queryDb class - for querying database.
    • QueryBuilderWrapper class - wrapping Enmvs\FluentPdo\Query query bulder class.

EntityContainer Class

<?php
/**
 * © Maleesha Gimshan - 2021 - github.com/maleeshagimshan98
 *
 * Entity Container Class
 */

 namespace Infinite\DataMapper;
 /**
  * Entity container
  */
 class EntityContainer 
 {

    /**
     * full qualified class name for the entity
     *
     * @var string
     */
    protected $base_class_name = "\Infinite\DataMapper\Entity\\";

    /**
     * database schema
     *
     * @var object
     */
    protected $dbSchema;

    /**
     * entities - [...,\Infinite\DataMapper\Entity\ENTITY_CLASS_NAME]
     *
     * @var array
     */
    protected $entities = [];


     /**
      * constructor
      */
     public function __construct ($entityDefinition = null)
     {
         if(!empty($entityDefinition))
         {
             $this->parseEntityDefinition($entityDefinition);
         }
     }

//...     

     /**
      * create an entity on the go
      *
      * @param string $name entity name
      * @return \Infinite\DataMapper\Entity entity object
      */
     public function entity ($name)
     {
         $entityDef = $this->getDefinition($name);
         if (!$entityDef)
         {
             throw new \Exception("Undefined_Entity");
         }
         $entityClass = $this->base_class_name."$name";
         return $this->entities[$name] = new $entityClass($entityDef,$name);         
     }

     /**
     * parse database entity definition
     *
     * @param [type] $schema JSON object defining the db schema
     * @return void
     */
    protected function parseEntityDefinition ($schema)
    {
        $arr = [];
        foreach ($schema as $key => $val) {
            $arr[$key] = $val;
        }
        $this->dbSchema = (object) $arr;
    }

    /**
     * get entity definitions
     *
     * @param string $name
     * @return mixed
     */
    protected function getDefinition ($name)
    { //echo json_encode($name);
        return $this->dbSchema->$name ?? false;
    }

    /**
     * checks if current entities count is zero
     *
     * @return boolean
     */
    public function is_empty () 
    {
        return count($this->entities) == 0;
    }


     /**
      * Get an entity
      *
      * @param string $entity Entity name
      * @return \Infinite\DataMapper\Entity object
      * @throws \Exception
      */
     public function get ($entity)
     {
        if (empty($this->entities[$entity])) {

            return $this->entity($entity);
            //throw new \Exception("Undefined_Entity");
        }
        return $this->entities[$entity];
     }

     /**
      * get all entities
      *
      * @return array
      */
     public function getAll () 
     {
         return $this->entities;
     }

     /**
      * set entity
      *
      * @param string $name
      * @param Infinite\DataMapper\Entity $entity entity object
      * @return void
      */
     public function set ($name,$entity)
     {
         $this->entities[$name] = $entity;                    
     }
 }

 ?>

For the context of this question other queryDb, QueryBuilderWrapper classes are not relevant, thus not shown here.

Entity Manager class

<?php
/**
 * © Maleesha Gimshan - 2021 - github.com/maleeshagimshan98
 *
 * Entity Manager class
 */

 namespace Infinite\DataMapper;

 include_once dirname(__DIR__)."/vendor/autoload.php";

// Entity classes extended from Entity base class
 include_once dirname(__DIR__)."/Products.php";  
 include_once dirname(__DIR__)."/sku.php";
 include_once dirname(__DIR__)."/product_sku.php";
 include_once dirname(__DIR__)."/orders.php";

 use Infinite\DataMapper\EntityContainer;
 use Infinite\DataMapper\QueryBuilder\QueryBuilderWrapper;
 use Infinite\DataMapper\QueryDb;
 use Infinite\DataMapper\Pagination;
 
 /**
  * Entity manager class
  */
 class EntityManager {

    /**
     * PDOConnection
     *
     * @var \PDO object
     */
    private $connection;

    /**
     * Entities
     *
     * @var Infinite\DataMapper\EntityContainer 
     *
     */
    public $entities;

    /**
     * current entity
     *
     * @var Infinite\DataMapper\Entity
     */
    protected $currentEntity;

    /**
     * current associated entities
     *
     * @var Infinite\DataMapper\EntityContainer
     */
    protected $currentAssociated;

    /**
     * sql statement created when querying entities
     *
     * @var \PDOStatement
     */
    protected $sqlStatement;

    /**
     * sql statements created when inserting data
     * have multiple sql statements
     * execute all in one transaction 
     *
     * @var array
     */
    protected $sqlStatementStack = [];

    /**
     * CRUD action - select,insert,update,delete
     *
     * @var string
     */
    protected $action = "";

    /**
     * constructor
     *
     * @param object $config
     * @return void
     */
    public function __construct (object $config)
    {        
        $this->initConfig($config);        
    }

    /**
     * initialize Entity manager with configuration
     *
     * @param object $config  configuration object
     * @return void
     * @throws \Exception
     */
    protected function initConfig (object $config)
    {
        if (empty($config->connection)) {
            throw new \Exception("Database_Connection_Not_Found");
        }
        
        $this->connection = $config->connection; //CONSIDER REMOVING THIS PROPERTY
        $this->entities = new EntityContainer(json_decode(file_get_contents(dirname(__DIR__)."/config/entity_definition.json")));
        $this->currentAssociated = new EntityContainer();
        $this->queryBuilder = new QueryBuilderWrapper($this->connection);
        $this->queryDb = new QueryDb($this->connection);
    }

    /**
     * return a new entity result object of a respective entity class - when inserting new data
     *
     * @param string $entity entity name
     * @return Infinite\DataMapper\Entity\EntityResult Entity result object
     * @throws \Exception
     */
    public function entity (string $entityName)
    {   
        return $this->entities->entity($entityName)->getEntityResult();        
    }

    /**
     * set current entity
     *
     * @param string $entity
     * @return void
     */
    protected function setCurrentEntity ($entity)
    {
        $this->currentEntity = $this->entities->get($entity);
    }

    /**
     * prepare basic parts of query string
     * for getting an entity from database
     *
     * @param \Infinite\DataMapper\Entity\ $entity entity object
     * @return void
     */
    protected function prepareGet ($entity)
    { /* TESING */ //echo \json_encode($entity);
        $this->sqlStatement = $this->queryBuilder->from($entity->table())
        ->select($entity->mapAttribsToColumn());
        return $this->sqlStatement;
    }

    /**
     * get entity from database
     *
     * @param string $entity entity name
     * @param array|null $id get a row based on id [column_name,value]
     * @return self
     * @throws \Exception
     */
    public function get ($entity,$id = null)
    {  
        $this->action = "select";
        $this->setCurrentEntity($entity); //echo json_encode($this->currentEntity);
        $sql = $this->prepareGet($this->currentEntity);
        if (!empty($id)) {
            $sql = $sql->where([
                $this->currentEntity->mapAttribsToColumn($id[0]) => $id[1]
            ]);        
        }
        return $this;
        /** TESTING */ //echo json_encode();
    }

    /**
     * associate entities in the result
     *
     * @param string $entity associated entity name
     * @param mixed  $joiningEntityId entity id (or foreign key) of associated entity
     * @return self
     * @throws \Exception
     */
    public function associate ($entity,$joiningEntityId = null)
    {
        $associated = $this->currentEntity->associations->get($entity);
        $target = $this->entities->get($associated->target);        
        $this->currentAssociated->set($associated->target,$target); //IMPORTANT - CHECK IF THIS IS NEEDED - $target
        
        $this->sqlStatement = $this->sqlStatement->leftJoin([
            $target->mapAttribsToColumn($associated->refer),
            $this->currentEntity->mapAttribsToColumn($associated->inverse)
            ])->select($target->mapAttribsToColumn());
        
            if (is_array($joiningEntityId))
            {
                $this->sqlStatement = $this->sqlStatement->where(
                    [
                        $target->mapAttribsToColumn($joiningEntityId[0]) => $joiningEntityId[1]
                    ]
                );
            }
            return $this;
    }

    /**
     * hydrate entity with data fetched from database
     *
     * @param array|object $res data fetched from database
     * @return void
     */
    protected function hydrate ($res) 
    {          
        $currentEntityName = $this->currentEntity->name();
        $entityCollection = [];
        foreach ($res as $key) 
        {
            $result = $this->entities->entity($currentEntityName)->hydrate($key);
            if (!$this->currentAssociated->is_empty())
            {
               $result = $this->hydrateAssociated($result,$key);
            }
            $entityCollection[] = $result;
        } //echo json_encode($entityCollection);
        return $entityCollection;
    }

    /**
     * hydrate associated entity of the current entity
     * (only single object from result set,
     * run this multiple times to hydrate all entities, and associated ones)
     *
     * @param Infinite\DataMapper\Entity $entity entiy object
     * @param array|object $data data fetched from database
     * @return Infinite\DataMapper\Entity
     */
    protected function hydrateAssociated ($entity,$data) 
    { 
        foreach ($this->currentAssociated->getAll() as $key => $value) 
        {
            $entity->set($key,$this->entities->entity($key)->hydrate($data));
        }
        return $entity;
    }

    /**
     * execute the current CRUD Action statement
     *
     * @return void
     */
    public function go ($data = null)
    {
        if ($this->action === 'select')
        {
            $res = $this->queryDb->select((object) [
                "sql" => $this->sqlStatement->getSqlString(),
                "data" => $data
            ]);
            /* TESTING */// echo $sql;
            /* TESTING */ //echo json_encode($res);
            return $this->hydrate($res); 
            //return $this->result($res);
        }
        if ($this->action === "insert")
        {
            $isDone = true; //CHECK
            foreach ($this->sqlStatementStack as $sql)
            {
                $this->queryDb->transaction();
                $isDone = $this->queryDb->insert((object)[
                    "sql" => $sql[0],
                    "data" => $sql[1]
                ]);
            }
            if ($isDone)
                {
                    $this->queryDb->commit();
                }
                else {
                    $this->queryDb->rollback();
                }     
        }
    }

    /**
     * save an entity to database
     *
     * @param EntityResult $entity entity object
     * @param array $values associated array of values - ["column_1" => "value", "column_2" => "value"]
     * @return void
     */
    public function save ($entity,$values)
    { 
        $this->action = "insert";
        $this->setCurrentEntity($entity->name());
        $entity->setFromArray($values);

        $sqlStatement[] = $this->queryBuilder->insert(
            $this->currentEntity->table(),
            $this->currentEntity->attribs_map->get(),
            $this->currentEntity->insertProps($values)
        )->getSqlString();

        $sqlStatement[] = $this->currentEntity->insertProps($values);
        $this->sqlStatementStack[] = $sqlStatement;
        //ONLY MAIN ENTITY IS BEIGN INSERTED
    }

    /**
     * execute raw sql statements directly
     * instead of using this ORM
     * 
     * @param string $sql sql query string
     * @param object $data data to be bound on the prepared query string
     * @return mixed
      */
    public function rawSql ($sql,$data)
    {

    }
 }

?>

Reasons for posting this question and what I'm looking for

  • Any improvements in the in-memory representation of relationships, entity attributes. (data structure)
  • Mostly I'm bothered about using foreach loops in most places.
  • Any improvement for the code and for the code structure is welcome.
  • What kind of improvements to be done to use in production?
\$\endgroup\$
5
  • \$\begingroup\$ The composer install or update is triggering error ` [RuntimeException] Could not scan for classes inside "EntityManager/" which does not appear to be a file nor a folder ` \$\endgroup\$
    – jona303
    Jun 4, 2021 at 8:46
  • \$\begingroup\$ @jona303 I can't figure out what the exact reason is, But a (dumb) quickfix would be remove those class map declarations and use composer install, then add them and use composer dumpautoload \$\endgroup\$ Jun 4, 2021 at 9:25
  • 1
    \$\begingroup\$ Please do not edit the question, especially the code after the question has been answered. You can ask a new question and link to the old one if they are related. Please read What do I do when someone answers \$\endgroup\$
    – pacmaninbw
    Jun 4, 2021 at 13:30
  • \$\begingroup\$ @pacmaninbw Sorry, I just read your comment after reverting back to my last revision... I will rollback to original question, post a new question and link to this \$\endgroup\$ Jun 4, 2021 at 14:02
  • 1
    \$\begingroup\$ Also, please do not include focus points (in how you'd like the code to be reviewed) in your title. You title should only describe what your code does. \$\endgroup\$ Jun 4, 2021 at 21:55

1 Answer 1

1
\$\begingroup\$

It's not related to your four questions but the most important thing for me is that your library is not (yet) an ORM from my point of view. An ORM is supposed to map Object and it's properties to a table and it's column. If I understand your github documentation there is not model object mapped to the database. It's more like an ORM configuration but without the mapping to the final object.

$product = $entityManager->entity("product");

// inserting values
$props = ["id" => "llmp_010", "product_name" => "test_product", "img_url" => "some_url"];

$entityManager->save($product,$props);

I see an array of properties, not an object with (encpsulated) properties that you can constraint.

The same apply here

// get data by a key

$data = $EntityManager->get('entity_name',['entity_attrib','some_value'])->go('some_value');

What am I supposed to get from the go method ?

\$\endgroup\$
5
  • \$\begingroup\$ Thank you for the answer, Yes, there have been few issues, and at the moment, I've updated my code to the latest on Github.. now EntityManager gets data from the EntityResult's (The object which contains all properties of entity) properties itself., You have to invoke go to actually execute the queries and get results, also when inserting, all are done in single transaction. (inspired by doctrine ORM) \$\endgroup\$ Jun 3, 2021 at 17:51
  • \$\begingroup\$ For the last part of your answer, that is to get an entity by a key (eg - primary key), and you have to pass the key to go() method too, currently. I think you made a good point, that passing args to go is unnecessary and does not make sense. I will change that (in get too) for sure, in the next commit. \$\endgroup\$ Jun 3, 2021 at 18:05
  • \$\begingroup\$ That's not the point I was spotting. The go method will return me an Object EntityResult that contains a property named props that contains the data from the DB. That's not what an ORM is supposed to do. Let's suppose we have a Product entity with a price property. The ORM is supposed to return an Instance of Product that has at least a public property price or better an encapsulated private property price with a getter and setter method. The advantages of the OOP are totally lost. \$\endgroup\$
    – jona303
    Jun 3, 2021 at 18:54
  • \$\begingroup\$ EntityResult is, something like a general container, which adds and hydrates those entity properties to EntityResult (based on the entity retrieved), (for example, product) and you can get and set them, and also currently these properties are public. That props property in EntityResult is for internal use only (it shoud be protected property, if my memory correct), not supposed to use by a user.. Please see update code. \$\endgroup\$ Jun 4, 2021 at 8:29
  • \$\begingroup\$ posted a new question in here because I've made changes to the code \$\endgroup\$ Jun 6, 2021 at 19:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.