<?php
namespace Phad;
/**
* Grants access to an item's node & loads item list
*/
class DataReader {
protected ?\PDO $pdo;
protected object $ItemInfo;
protected array $data_node_info;
protected ?\Phad $phad;
public function __construct(array $data_node_info, object $ItemInfo, ?\PDO $pdo=null, ?\Phad $phad=null){
$this->ItemInfo = $ItemInfo;
$this->data_node_info = $data_node_info;
$this->pdo = $pdo;
$this->phad = $phad;
}
/**
* Query for a row with id matching `$_GET['id']`, return a `Phad\BlackHole` object if no id passed, or return `$_POST` if mode is `FORM_SUBMIT`.
*
* @return array containing rows. Each row is an array.
*/
public function get_default_form_data(){
if ($this->ItemInfo->mode==\Phad\Blocks::FORM_SUBMIT){
return [$_POST];
} else if (!isset($_GET['id'])){
return [ [ '_object'=> new \Phad\BlackHole() ] ];
}
$table = strtolower($this->ItemInfo->name);
$stmt = $this->pdo->prepare("SELECT * FROM `{$table}` WHERE `id` = :id");
$stmt->bindParam(':id', $_GET['id'], \PDO::PARAM_INT);
$stmt->execute();
$rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);
return $rows;
}
/**
* Execute the data loader declared on the data node, and return the result.
*/
public function get_data_loader_rows(){
$key = $this->data_node_info['data_loader'];
if (!isset($this->phad->data_loaders[$key])){
throw new \Exception("There is no data loader for key '$key'. Set `\$phad->data_loaders['$key']` to a callable that accepts (array \$data_node_info, object \$ItemInfo, \Phad \$phad) & returns an array of rows.");
}
$rows = ($this->phad->data_loaders[$key])($this->data_node_info, $this->ItemInfo, $this->phad);
return $rows;
}
/**
* Build a query based on the data node, execute it, and return the rows
*
*/
public function get_queried_rows(){
$binds = null;
$sql = static::build_sql(strtolower($this->ItemInfo->name), $this->data_node_info, $this->ItemInfo->args, $binds);
$stmt = $this->pdo->prepare($sql);
$stmt->execute($binds);
$rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);
return $rows;
}
/**
* Process the ItemInfo and Data Node, and return the appropriate data.
*
* @return array of rows. (*each row is an array*)
*/
public function get_rows(): array {
$item_name = $this->ItemInfo->name;
$args = $this->ItemInfo->args;
// Return data explicitly passed in
if (isset($args[$item_name.'List'])){
return $args[$item_name.'List'];
} else if (isset($args[$item_name])){
return [ $args[$item_name] ];
} else if (isset($args['_object'])){
return [ ['_object' => $args['_object']] ];
}
// type is one of null, 'default', 'black_hole'
$data_type = $this->data_node_info['type'];
if ($data_type == null){
if (isset($this->data_node_info['data_loader'])){
$data_type = 'data_loader';
} else {
$data_type = 'mysql';
}
}
$item_type = $this->ItemInfo->type;
switch ($data_type){
case \Phad\enum\DataTypes::DEFAULT->value:
if ($item_type=='form'){
return $this->get_default_form_data();
}
return [];
case \Phad\enum\DataTypes::DATA_LOADER->value:
return $this->get_data_loader_rows();
case \Phad\enum\DataTypes::BLACK_HOLE->value:
return [ [ '_object'=> new \Phad\BlackHole() ] ];
case \Phad\enum\DataTypes::MYSQL->value:
return $this->get_queried_rows();
default:
throw new \Exception(sprintf(\Phad\Errors::DATA_TYPE_NOT_SUPPORTED, $data_type));
}
}
/**
* Build an SQL query and an array of bindable key/value pairs to pass to `PDOSatement::execute($binds)`
*
* If your SQL contains bindable params like `:get.id`, then this will be filled by `$_GET['id']`. Bindable params like `:some_name` are filled by `$args['some_name']`
*
* @param $table_name string the SQL Table name
* @param $data_node_info array an array representation of a data node listing 'sql' as a raw query or SQL features 'cols', 'where', 'limit', 'orderby', and 'join'.
* @param $args array of arguments to bind to the query.
* @param &$binds Creates an array you can pass to `PDOStatement::execute($binds)` Values are filled from `$args` or set `null` if not present in `$args`.
*/
static public function build_sql(string $table_name, array $data_node_info, array $args, &$binds){
$table = "`$table_name`";
$binds = [];
if (isset($data_node_info['sql'])){
$sql = $data_node_info['sql'];
} else {
// $cols = '*';
$cols = empty($data_node_info['cols']) ? '*' : $data_node_info['cols'];
$where = empty($data_node_info['where']) ? ' ' : "\nWHERE ".$data_node_info['where'].' ';
$orderby = empty($data_node_info['orderby']) ? '' : "\nORDER BY ".$data_node_info['orderby'].' ';
$join = empty($data_node_info['join']) ? '' : "\nJOIN ".$data_node_info['join'].' ';
if (isset($data_node_info['paginate'])){
$size = intval($data_node_info['paginate']);
$current_page = intval($_GET['page']??0);
$limit_start = $size * $current_page;
$limit = "\nLIMIT ".$limit_start.", ".$size." ";
} else if (isset($data_node_info['limit'])){
$limit = empty($data_node_info['limit']) ? '' : "\nLIMIT ".$data_node_info['limit'].' ';
} else {
$limit = '';
}
$sql = "SELECT {$cols} FROM {$table} {$join}{$where}{$orderby}{$limit}";
}
preg_match_all('/ \:([a-zA-Z\_\.]+)(\r|\n|\s|$)/', $sql.' ', $matches);
foreach ($matches[1] as $col){
$parts = explode('.', $col);
if (count($parts)==2&&$parts[0]=='get'){
$key = str_replace('.','_', $col);
$sql = str_replace($col, $key, $sql);
$col = $key;
$args[$key] = $_GET[$parts[1]] ?? null;
}
$binds[':'.$col] = $args[$col] ?? null;
}
return $sql;
}
}