<commit_before><?php
namespace Pragma\ORM;

use Pragma\DB\DB;
use \PDO;

class Model extends QueryBuilder implements SerializableInterface{
	static protected $table_desc = array();

	protected $fields = array();
	protected $new = true;
	protected $desc = array();
	protected $inclusions = array();

	//Hooks
	protected $before_save_hooks = [];
	protected $after_save_hooks = [];
	protected $before_delete_hooks = [];
	protected $after_delete_hooks = [];
	protected $after_open_hooks = [];
	protected $initialized = false;//usefull for sub-traits
	protected $initial_values = [];

	protected $primary_key = 'id'; //mixed - string or array of strings

	protected $default_matchers = null;
	protected $default_loaders = null;

	protected $forced_id_allowed = false;

	public function __construct($tb_name, $pk = null){
		parent::__construct($tb_name);
		$this->fields = $this->describe();
		$error = false;
		if( is_null($pk) || ! is_array($pk) ){
			if( is_null($pk) ){
				$pk = 'id';
			}
			if( array_key_exists('id', $this->fields)){
				$this->primary_key = 'id';
			}
			else{
				$error = true;
			}
		}
		else{
			foreach($pk as $k){
				if( ! array_key_exists('id', $this->fields) ){
					$error = true;
					break;
				}
			}
			if(!$error){
				$this->primary_key = $pk;
			}
		}

		if($error){
			throw new \Exception("Error getting an instance of ".get_class($this)." - PK Error", 1);

		}
	}

	public function allowForcedId($val = true){
		$this->forced_id_allowed = $val;
		return $this;
	}

	public function __get($attr){
		if(array_key_exists($attr, $this->describe())){
			return $this->fields[$attr];
		}elseif(array_key_exists($attr, $this->inclusions)){
			return $this->inclusions[$attr];
		}
		return null;
	}

	public function __set($attr, $value){
		if(array_key_exists($attr, $this->describe())){
			$this->fields[$attr] = $value;
		}
		return $this;
	}

	public function __isset($attr) {
		if(array_key_exists($attr, $this->describe())){
			return (false === empty($this->fields[$attr]));
		}
		return null;
	}

	public function is_new(){
		return $this->new;
	}

	public function get_table(){
		return $this->table;
	}

	public function get_primary_key(){
		return $this->primary_key;
	}

	public function open($pk){
		$db = DB::getDB();
		$sql = "SELECT * FROM ".$this->table." WHERE ";
		$params = [];

		if( ! is_array($pk) && ! is_array($this->primary_key) ){
			$sql .= $this->primary_key ." = :pk";
			$params[':pk'] = $pk;
		}
		else if (is_array($pk) && is_array($this->primary_key)){
			$i = 1;
			$mypks = array_flip($this->primary_key);
			foreach($pk as $k => $val){
				if( ! isset($mypks[$k]) ){
					throw new \Exception("Error opening the instance of ".get_class($this)." - unknown PK column", 1);
					break;
				}
				if( $i > 1 ){
					$sql .= " AND ";
				}

				$sql .= " $k = :pk$i ";
				$params[":pk$i"] = $val;
				$i++;
			}
		}
		else{
			throw new \Exception("Error opening the instance of ".get_class($this)." - wrong pk signature", 1);
		}

		$res = $db->query($sql, $params);
		$data = $db->fetchrow($res);
		if ($data) {
			$this->openWithFields($data);
			//don't play after_open_hooks here, it will be played in openWithFields
			return $this;
		}

		return null;
	}

	public static function find($pk){

		$o = new static();

		if( ! is_array($pk) && ! is_array($o->primary_key)){
			$obj = static::forge()->where($o->primary_key, '=', $pk)->first();
		}
		else if (is_array($pk) && is_array($o->primary_key)){
			$qb = static::forge();
			$mypks = array_flip($o->primary_key);
			foreach($pk as $k => $val){
				if( ! isset($mypks[$k]) ){
					throw new \Exception("Error opening the instance of ".get_class($o)." - unknown PK column", 1);
					break;
				}
				$qb->where($k, '=', $val);
			}

			$obj = $qb->first();
		}
		else{
			throw new \Exception("Error opening the instance of ".get_class($o)." - wrong pk signature", 1);
		}

		//don't play after_open_hooks here, it will be played in the $qb->first via openWithFields
		return $obj;
	}

	public function openWithFields($data, $whitelist = null){
		if( ! empty($data) && isset($data['id']) ){

			//whitelist allows to get the description on an object and check if data is correct
			//the idea is to optimize by doing the describe only once
			if( ! is_null($whitelist) ){
				foreach($data as $f => $val){
					if( ! array_key_exists($f, $whitelist) ){
						unset($data[$f]);
					}<commit_msg>Model::openWithFields() ckeck if all primary keys are in data<commit_after><?php
namespace Pragma\ORM;

use Pragma\DB\DB;
use \PDO;

class Model extends QueryBuilder implements SerializableInterface{
	static protected $table_desc = array();

	protected $fields = array();
	protected $new = true;
	protected $desc = array();
	protected $inclusions = array();

	//Hooks
	protected $before_save_hooks = [];
	protected $after_save_hooks = [];
	protected $before_delete_hooks = [];
	protected $after_delete_hooks = [];
	protected $after_open_hooks = [];
	protected $initialized = false;//usefull for sub-traits
	protected $initial_values = [];

	protected $primary_key = 'id'; //mixed - string or array of strings

	protected $default_matchers = null;
	protected $default_loaders = null;

	protected $forced_id_allowed = false;

	public function __construct($tb_name, $pk = null){
		parent::__construct($tb_name);
		$this->fields = $this->describe();
		$error = false;
		if( is_null($pk) || ! is_array($pk) ){
			if( is_null($pk) ){
				$pk = 'id';
			}
			if( array_key_exists('id', $this->fields)){
				$this->primary_key = 'id';
			}
			else{
				$error = true;
			}
		}
		else{
			foreach($pk as $k){
				if( ! array_key_exists('id', $this->fields) ){
					$error = true;
					break;
				}
			}
			if(!$error){
				$this->primary_key = $pk;
			}
		}

		if($error){
			throw new \Exception("Error getting an instance of ".get_class($this)." - PK Error", 1);

		}
	}

	public function allowForcedId($val = true){
		$this->forced_id_allowed = $val;
		return $this;
	}

	public function __get($attr){
		if(array_key_exists($attr, $this->describe())){
			return $this->fields[$attr];
		}elseif(array_key_exists($attr, $this->inclusions)){
			return $this->inclusions[$attr];
		}
		return null;
	}

	public function __set($attr, $value){
		if(array_key_exists($attr, $this->describe())){
			$this->fields[$attr] = $value;
		}
		return $this;
	}

	public function __isset($attr) {
		if(array_key_exists($attr, $this->describe())){
			return (false === empty($this->fields[$attr]));
		}
		return null;
	}

	public function is_new(){
		return $this->new;
	}

	public function get_table(){
		return $this->table;
	}

	public function get_primary_key(){
		return $this->primary_key;
	}

	public function open($pk){
		$db = DB::getDB();
		$sql = "SELECT * FROM ".$this->table." WHERE ";
		$params = [];

		if( ! is_array($pk) && ! is_array($this->primary_key) ){
			$sql .= $this->primary_key ." = :pk";
			$params[':pk'] = $pk;
		}
		else if (is_array($pk) && is_array($this->primary_key)){
			$i = 1;
			$mypks = array_flip($this->primary_key);
			foreach($pk as $k => $val){
				if( ! isset($mypks[$k]) ){
					throw new \Exception("Error opening the instance of ".get_class($this)." - unknown PK column", 1);
					break;
				}
				if( $i > 1 ){
					$sql .= " AND ";
				}

				$sql .= " $k = :pk$i ";
				$params[":pk$i"] = $val;
				$i++;
			}
		}
		else{
			throw new \Exception("Error opening the instance of ".get_class($this)." - wrong pk signature", 1);
		}

		$res = $db->query($sql, $params);
		$data = $db->fetchrow($res);
		if ($data) {
			$this->openWithFields($data);
			//don't play after_open_hooks here, it will be played in openWithFields
			return $this;
		}

		return null;
	}

	public static function find($pk){

		$o = new static();

		if( ! is_array($pk) && ! is_array($o->primary_key)){
			$obj = static::forge()->where($o->primary_key, '=', $pk)->first();
		}
		else if (is_array($pk) && is_array($o->primary_key)){
			$qb = static::forge();
			$mypks = array_flip($o->primary_key);
			foreach($pk as $k => $val){
				if( ! isset($mypks[$k]) ){
					throw new \Exception("Error opening the instance of ".get_class($o)." - unknown PK column", 1);
					break;
				}
				$qb->where($k, '=', $val);
			}

			$obj = $qb->first();
		}
		else{
			throw new \Exception("Error opening the instance of ".get_class($o)." - wrong pk signature", 1);
		}

		//don't play after_open_hooks here, it will be played in the $qb->first via openWithFields
		return $obj;
	}

	public function openWithFields($data, $whitelist = null){
		if( ! empty($data) ){

			// Check if all primary keys are in $data else we can't correctly save/update object
			if(is_array($this->primary_key)){
				foreach($this->primary_key as $k){
					if(!isset($data[$k])){
						return null;
					}
				}
			}elseif(!isset($data[$this->primary_key])){
				return null;
			}

			//whitelist allows to get the description on an object and check if data is correct
			//the idea is to optimize by doing the describe only once
			if( ! is_null($whitelist) ){
				foreach($data as $f => $val){
					if( ! array_key_exists($f, $whitelist) ){
						unset($data[$f]);
					}
{'fraction_non_alphanumeric': 0.18184796854521626, 'fraction_numerical': 0.0015290519877675841, 'mean_word_length': 3.466829268292683, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 84, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4375675', 'n_tokens_mistral': 3735, 'n_tokens_neox': 3205, 'n_words': 789}