I've written a backend for a chat app but I'm not sure, if I'm following all the SOLID principles and if my code is clean enough.IModel interface for my db entities:interface IModel{    /**     * get a specific field from the model.     * @param string $fieldName name of the field to return     * @return mixed;     */    function getField($fieldName);    /**     * get the table name of the model.     * @return string;     */    function getTableName();    /**     * get the primary key of the models table.     * @return string;     */    function getPrimaryKey();    /**     * get the model data as an object.     * @return object;     */    function getAsObject();    /**     * get the model data as an array.     * @return array;     */    function getAsArray();    /**     * initilize with an array.     * @param array $model to initiate model with     */    function initArr($model);}Abstract class for models:/** * Class Model * Class to define datasource entities * @package Simplechat\Models */abstract class Model implements IModel{    /**     * name of the table that this model represents     * @var string     */    protected $tableName;    /**     * primary key of the table that this model represents     * @var string     */    protected $primaryKey;    /**     * Model constructor.     * When model is initiated, it can be filled with array data.     * @param array $data     */    public function __construct($data)    {        $this->initArr($data);    }    /**     * get the table name of the model.     * @return string;     */    function getTableName()    {        return $this->tableName;    }    /**     * get the primary key of the models table.     * @return string;     */    function getPrimaryKey()    {        return $this->primaryKey;    }    /**     * get a specific field from the model.     * @param string $fieldName name of the field to return     * @return mixed;     */    public function getField($fieldName){        try {            return $this->{$fieldName};        } catch (\Exception $e) {            return ;        }    }    /**     * get the model data as an object.     * @return object;     */    public function getAsObject(){        return (Object)$this->getAsArray();    }    /**     * get the model data as an array.     * @return array;     */    abstract public function getAsArray();    /**     * initilize with an array.     * @param array $model Model array to initiate model with     */    abstract public function initArr($model);}2 Models extended from Model, MessageModel:/** * Class MessageModel * Class to handle Message entity * @package Simplechat\Models */class MessageModel extends Model{    /**     * primary key message id     * @var integer     */    private $messageId;    /**     * content of the message     * @var string     */    private $content;    /**     * timestamp of the message     * @var integer     */    private $timestamp;    /**     * senders user id     * @var integer     */    private $senderId;    /**     * receivers user id     * @var integer     */    private $receiverId;    /**     * if the message is displayed to the receiver     * @var bool     */    private $displayed;    /**     * MessageModel constructor.     * Initializing properties     * @param array $data     */    public function __construct(array $data)    {        parent::__construct($data);        $this->tableName = messages;        $this->primaryKey = messageId;    }    /**     * initilize with an array.     * @param array $message Message array to initiate model with     */    public function initArr($message)    {        $this->messageId = isset($message['messageId']) ? $message['messageId'] : null;        $this->content =  isset($message['content']) ? $message['content'] : null;        $this->timestamp = isset($message['timestamp']) ? $message['timestamp'] : null;        $this->senderId =  isset($message['senderId']) ? $message['senderId'] : null;        $this->receiverId = isset($message['receiverId']) ? $message['receiverId'] : null;        $this->displayed =  isset($message['displayed']) ? $message['displayed'] : null;    }    /**     * get the model data as an array.     * @return array;     */    public function getAsArray(){        $array = array();        if(isset($this->messageId))            $array['messageId'] = $this->messageId;        if(isset($this->content))            $array['content'] = $this->content;        if(isset($this->timestamp))            $array['timestamp'] = $this->timestamp;        if(isset($this->senderId))            $array['senderId'] = $this->senderId;        if(isset($this->receiverId))            $array['receiverId'] = $this->receiverId;        if(isset($this->displayed))            $array['displayed'] = $this->displayed;        return $array;    }    /**     * get field displayed     * @return bool     */    public function getDisplayed()    {        return $this->displayed;    }    /**     * set field displayed     * @param $displayed     */    public function setDisplayed($displayed)    {        $this->displayed = $displayed;    }    /**     * get field senderId     * @return int     */    public function getSenderId()    {        return $this->senderId;    }    /**     * set field senderId     * @param $senderId     */    public function setSenderId($senderId)    {        $this->senderId = $senderId;    }}UserModel:/** * Class UserModel * Class to handle User entity * @package Simplechat\Models */class UserModel extends Model{    /**     * primary key user id     * @var integer     */    private $userId;    /**     * user name     * @var string     */    private $name;    /**     * UserModel constructor.     * @param array $data     */    public function __construct(array $data)    {        parent::__construct($data);        $this->tableName = users;        $this->primaryKey = userId;    }    /**     * initilize with an array.     * @param array $user User array to initiate model with     */    public function initArr($user)    {        $this->userId = isset($user['userId']) ? $user['userId'] : null;        $this->name =  isset($user['name']) ? $user['name'] : null;    }    /**     * get the model data as an array.     * @return array;     */    public function getAsArray()    {        $array = array();        if($this->userId)            $array['userId'] = $this->userId;        if($this->name)            $array['name'] = $this->name;        return $array;    }    /**     * getter for field name     * @return string     */    public function getName()    {        return $this->name;    }    /**     * setter for field name     * @param $name     */    public function setName($name)    {        $this->name = $name;    }}I also have a hierarchy for datasources in case I'd need more than one.Interface IDataSource:/** * Interface IDataSource * Interface to define a template for datasources * @package Simplechat\Models */interface IDataSource{    /**     * All DataSources should implement connect to connect the datasource.     * @return mixed     */    public function connect();    /**     * All DataSources should implement reading from the datasource.     * @param IModel $model     * @param integer $primaryId     * @return IModel     */    public function readOne(IModel $model, $primaryId);    /**     * All DataSources should implement reading with custom conditions from the datasource.     * @param IModel $model     * @param array $conditions     * @return array     */    public function readBy(IModel $model, $conditions);    /**     * All DataSources should implement creating from the datasource.     * @param IModel $model     * @return mixed     */    public function create(IModel $model);    /**     * All DataSources should implement update from the datasource.     * @param IModel $model     * @return mixed     */    public function update(IModel $model);}Abstract class DataSource:/** * Class DataSource * Class to allow shared methods for different datasources * @package Models */abstract class DataSource implements IDataSource{    /**     * database resource.     * @var mixed     */    protected $db;    /**     * All DataSources should implement connect to connect the datasource.     * @return mixed     */    abstract public function connect();    /**     * All DataSources should implement reading with primary key from the datasource.     * @param IModel $model     * @param integer $primaryId     * @return IModel     */    abstract public function readOne(IModel $model, $primaryId);    /**     * All DataSources should implement reading with custom conditions from the datasource.     * @param IModel $model     * @param array $conditions     * @return array     */    abstract public function readBy(IModel $model, $conditions);    /**     * All DataSources should implement creating from the datasource.     * @param IModel $model     * @return mixed     */    abstract public function create(IModel $model);    /**     * All DataSources should implement update from the datasource.     * @param IModel $model     * @return mixed     */    abstract public function update(IModel $model);    /**     * All DataSources should return their connection variable.     * @return mixed     */    public function getConnection()    {        return $this->db;    }}Class for SQLite connections:/** * Class SQLiteDataSource * Class to handle SQLite connections * @package Simplechat\Models */class SQLiteDataSource extends DataSource{    /**     * connecting to sqlite datasource.     * @return void     */    public function connect()    {        $this->db = new \SQLite3('db/simplechat.db');    }    /**     * Read a specific row from SQLite database and return an IModel.     * @param IModel $model     * @param integer $primaryId     * @return IModel     */    public function readOne(IModel $model, $primaryId)    {        $query = 'SELECT * FROM ' . $model->getTableName() .  WHERE  . $model->getPrimaryKey() .  = :userId ;        $stmt = $this->db->prepare($query);        $stmt->bindValue(':userId', $primaryId, SQLITE3_INTEGER);        $result = $stmt->execute();        $model->initArr($result->fetchArray());        return $model;    }    /**     * All DataSources should implement reading with custom conditions from the datasource.     * @param IModel $model     * @param array $conditions     * @return array     */    public function readBy(IModel $model, $conditions)    {        $query = 'SELECT * FROM ' . $model->getTableName() .  WHERE ;        $queryArr = array();        foreach($conditions as $key => $value)        {            $queryArr[] = $key .  = : . $key;        }        $query .= implode( AND , $queryArr);        $stmt = $this->db->prepare($query);        foreach($conditions as $key => $value)        {            $stmt->bindValue(':' . $key, $value);        }        $result = $stmt->execute();        $response = array();        while($returnData = $result->fetchArray())        {            $model->initArr($returnData);            $response[] = clone $model;        }        return $response;    }    /**     * Create a new row in the database for the given IModel.     * @param IModel $model     * @return mixed     */    public function create(IModel $model)    {        $array = $model->getAsArray();        $query = INSERT INTO  . $model->getTableName() .  ( . implode(,,array_keys($array)) . ) VALUES (: . implode(,:,array_keys($array)) . );        $stmt = $this->db->prepare($query);        foreach($array as $key => $value)        {            $stmt->bindValue(':' . $key, $value);        }        $stmt->execute();        return $this->db->lastInsertRowID();    }    /**     * Update a specific row in the database for the given IModel.     * @param IModel $model     * @return mixed     */    public function update(IModel $model)    {        $array = $model->getAsArray();        $query = UPDATE  .$model->getTableName() .  SET ;        $queryArr = array();        foreach($array as $key => $value)        {            $queryArr[] = $key .  = : . $key;        }        $query .= implode(,, $queryArr) .  WHERE  . $model->getPrimaryKey() .  = : . $model->getPrimaryKey() . ;        $stmt = $this->db->prepare($query);        foreach($array as $key => $value)        {            $stmt->bindValue(':' . $key, $value);        }        return is_object($stmt->execute());    }}These classes are used in my main controller:/** * Class ChatController * Handling all the required behaviour for the chat api * @package Simplechat\Controllers */class ChatController{    /**     * property for handling datasource interactions     * @var IDataSource     */    private $db;    /**     * ChatController constructor.     * initializing the datasource     * @param IDataSource $db     */    public function __construct(IDataSource $db) {        $this->db = $db;        $this->db->connect();    }    /**     * create a new user in the datasource     * @param string $name name of the user     * @return mixed     */    public function createUser($name)    {        return $this->db->create(new UserModel(array(name => $name)));    }    /**     * create a new message to the receiver from sender     * @param string $content message content     * @param int $senderId sender user id     * @param int $receiverId receiver user id     * @return mixed     */    public function sendMessage($content, $senderId, $receiverId)    {        return $this->db->create(            new MessageModel(                array(                    content => $content,                    timestamp=>time(),                    senderId => $senderId,                    receiverId => $receiverId,                    displayed => 0                )            )        );    }    /**     * get and return messages which are not displayed to the current user yet     * @param int $receiverId receiver user id     * @return array     */    public function getNewMessages($receiverId)    {        $result = $this->db->readBy(new MessageModel(array()),array(receiverId => $receiverId,displayed => 0));        $response = array();        foreach($result as $message)        {            $message->setDisplayed(1);            $this->db->update($message);            $user = $this->db->readOne(new UserModel(array()),$message->getSenderId());            $data = $message->getAsArray();            $data['name'] = $user->getName();            $response[] = json_encode($data);        }        return $response;    }}Am I doing anything wrong, frowned upon or smelly here?
Chat Application
php;object oriented;sqlite;chat
In this case, I am not seeing great value in breaking your interfaces apart from your base class for Model and DataSource.  To me, in order for you to consider an interface, that interface must have applicability to multiple classes which would implement it.  I don't think that is the case here, and your interface names themselves should be red flag to you, as their names are EXACTLY the same as your abstract base class names, prepended with an I.Now if you had some behavior that you wanted to attach to each model like, for example, making them JSON-serializable, that might be an interesting use for an interface.I would recommend always being explicit around method visibility, some of your methods do not have this.public function getField($fieldName){    try {        return $this->{$fieldName};    } catch (\Exception $e) {        return ;    }}This doesn't make sense.  Why would $this->{$fieldName} throw?  I also don't know that I like implementing a dynamic getter like this.  I would think that you would want classes inheriting Model to be specific about creating their own getters.I don't like your model's getAsObject() and getAsArray() methods in their current form, or perhaps not their naming at least. The concrete model, will, after all, be an object.  So is what you really want to do simply extract stdClass() or associative array representations? If so perhaps method names like toStdClass(), toArray(), toJson(), etc. might be more meaningful ways to name methods that give certain renderings of the concrete Model (and also perhaps a good serialization interface that you are wanting your models to implement) where MySerializationInterface might be defined as having methods like:public function toStdClass();public function toArray();public function toJson();public function toXML();// whatever other interchange formats you want your models to support.I don't like the mix of in one case, having this method abstract and in the other having a concrete method. I would think you should either provide standard implementation of both or neither.I honestly think you can probably generalize implementation in the abstract class with something like get_object_vars().  Your current concrete implementations seem like a lot of code for little value.public function toArray() {    return get_object_vars($this);}public function toStdClass() {    return (object) get_object_vars($this);}Should initArray() really be named something like createFromArray() that is more meaningful?I think your dependencies are totally inverted.  You are treating the Model object as a dependency to the DataSource object, when these should probably be reversed.   Why does a DataSource object need to have knowledge on how to retrieve a model from the database (i.e. what queries to form)?I don't really understand you pattern of instantiating a model first, and then passing that model to the DataSource which then initializes the model and returns it? This seems like an odd initialization sequence.  Why not just give DataSource to the model as a dependency for object initialization?You would then implement all the logic in the Model classes around how to populate/instantiate itself from the DataSource.You might even want to consider a separate model factory class to manage model instantiation or, at a minimum, placing static methods on your model classes to do things like instantiate a model given a primary key id.  A public constructor for a model might not even make sense for your application.As a caller to this code, I would like to see something like:$id = /* some value */;$sqlite = new SQLiteDataSource();// instantiate user object based on id,// setting the datasource depedency on the object$user = UserModel::readById($id, $sqlite);// change name and update to data store$user->setName('some value');$user->update();// or for object creation from array of data// this method would also write object to data store$new_user = UserModel::createFromArray($array, $sqlite);// get the id of newly created user$new_user_id = $new_user->getId();When you get to a usage pattern like this, the need for interfaces becomes more clear.  You might for example have your model classes that implement DataSourceCRUD interface or similar, where Model or inheriting class would need to implement methods like:public static function readById($id, DataSource $datasource);public static function createFromArray($data_array, DataSource $datasource);public static function deleteById($id, DataSource $datasource);public function update(); // only makes sense in object contextpublic function delete(); // delete from object context