Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Constants/PicoHttpStatus.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

namespace MagicObject\Constants;

/**
Expand Down
6 changes: 3 additions & 3 deletions src/DataLabel/PicoDataLabel.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
class PicoDataLabel extends SetterGetter
{
const ANNOTATION_PROPERTIES = "Properties"; // Annotation key for properties
const ANNOTATION_TABLE = "Table"; // Annotation key for table name
const KEY_NAME = "name"; // Key for the name
const ANNOTATION_VAR = "var"; // Annotation key for variable
const ANNOTATION_TABLE = "Table"; // Annotation key for table name
const KEY_NAME = "name"; // Key for the name
const ANNOTATION_VAR = "var"; // Annotation key for variable

/**
* Parameters defined in the class annotations.
Expand Down
96 changes: 69 additions & 27 deletions src/DataTable.php
Original file line number Diff line number Diff line change
Expand Up @@ -338,77 +338,119 @@ private function label($reflexProp, $parameters, $key, $defaultLabel)
}

/**
* Appends table rows based on class properties.
*
* This method generates rows for the table based on the properties of the class
* and appends them to the provided DOM node.
* Appends table rows based on class properties using reflection and annotations.
*
* @param DOMDocument $doc The DOM document used to create elements.
* @param DOMNode $tbody The DOM node representing the <tbody> of the table.
* @param array $props Array of ReflectionProperty objects representing class properties.
* @param ReflectionProperty[] $props Array of ReflectionProperty objects.
* @param string $className Name of the class for reflection.
* @return self Returns the current instance for method chaining.
*/
private function appendByProp($doc, $tbody, $props, $className)
{
foreach ($props as $prop) {
$key = $prop->name;
$label = $key;
$value = $this->get($key);
if (is_scalar($value)) {
$tr = $tbody->appendChild($doc->createElement(self::TAG_TR));

$reflexProp = new PicoAnnotationParser($className, $key, PicoAnnotationParser::PROPERTY);

if ($reflexProp != null) {
$parameters = $reflexProp->getParametersAsObject();
if ($parameters->issetLabel()) {
$label = $this->label($reflexProp, $parameters, $key, $label);
// Inclusion of null values often necessary for table structure consistency
if (is_scalar($value) || $value === null) {
$label = $key;

// Parse annotations for custom label
try {
$reflexProp = new PicoAnnotationParser($className, $key, PicoAnnotationParser::PROPERTY);
if ($reflexProp != null) {
$parameters = $reflexProp->getParametersAsObject();
// Assuming issetLabel() or similar check exists in your annotation parser
if (method_exists($parameters, 'issetLabel') && $parameters->issetLabel()) {
$label = $this->label($reflexProp, $parameters, $key, $label);
}
}
} catch (Exception $e) {
// Fallback to property name if annotation parsing fails
$label = $key;
}

$td1 = $tr->appendChild($doc->createElement(self::TAG_TD));
// Create Row
$tr = $doc->createElement(self::TAG_TR);
$tbody->appendChild($tr);

// Column 1: Label
$td1 = $doc->createElement(self::TAG_TD);
$td1->setAttribute(self::KEY_CLASS, self::TD_LABEL);
$td1->textContent = $label;
$td1->appendChild($doc->createTextNode($label));
$tr->appendChild($td1);

$td2 = $tr->appendChild($doc->createElement(self::TAG_TD));
// Column 2: Value
$td2 = $doc->createElement(self::TAG_TD);
$td2->setAttribute(self::KEY_CLASS, self::TD_VALUE);
$td2->textContent = isset($value) ? $value : "";

// Safe formatting for boolean/null/strings
$displayValue = $this->formatScalarValue($value);
$td2->appendChild($doc->createTextNode($displayValue));
$tr->appendChild($td2);
}
}
return $this;
}

/**
/**
* Appends table rows based on provided values.
*
* This method takes an array of values and creates rows in the table,
* appending them to the provided DOM node.
*
* @param DOMDocument $doc The DOM document used to create elements.
* @param DOMNode $tbody The DOM node representing the <tbody> of the table.
* @param stdClass $values Data to append as rows.
* @param array|stdClass $values Data to append as rows.
* @return self Returns the current instance for method chaining.
*/
private function appendByValues($doc, $tbody, $values)
{
if (empty($values)) {
return $this;
}

foreach ($values as $propertyName => $value) {
if (is_scalar($value)) {
$tr = $tbody->appendChild($doc->createElement(self::TAG_TR));
$label = $this->getLabel($propertyName);
// Check if value can be displayed as string
if (is_scalar($value) || $value === null) {
$tr = $doc->createElement(self::TAG_TR);
$tbody->appendChild($tr);

$td1 = $tr->appendChild($doc->createElement(self::TAG_TD));
$label = $this->getLabel($propertyName);

// Column 1: Label
$td1 = $doc->createElement(self::TAG_TD);
$td1->setAttribute(self::KEY_CLASS, self::TD_LABEL);
$td1->textContent = $label;
// Use createTextNode for safer character handling
$td1->appendChild($doc->createTextNode($label));
$tr->appendChild($td1);

$td2 = $tr->appendChild($doc->createElement(self::TAG_TD));
// Column 2: Value
$td2 = $doc->createElement(self::TAG_TD);
$td2->setAttribute(self::KEY_CLASS, self::TD_VALUE);
$td2->textContent = isset($value) ? $value : "";

// Format boolean or null if necessary
$displayValue = $this->formatScalarValue($value);
$td2->appendChild($doc->createTextNode($displayValue));
$tr->appendChild($td2);
}
}
return $this;
}

/**
* Helper to ensure value is safe string for DOM
* @param mixed $value
* @return string
*/
private function formatScalarValue($value)
{
if ($value === true) return 'true';
if ($value === false) return 'false';
return (string)(isset($value) ? $value : "");
}

/**
* Gets the label for a specified property.
*
Expand Down
15 changes: 9 additions & 6 deletions src/Database/PicoDatabasePersistence.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ class PicoDatabasePersistence // NOSONAR
const COMMA_RETURN = ", \r\n";
const INLINE_TRIM = " \r\n\t ";
const ALWAYS_TRUE = "(1=1)";

const IS_NULL = " is null";
const CLAUSE_AND = " and ";

/**
* Database connection
Expand Down Expand Up @@ -929,7 +932,7 @@ private function getWhere($info, $queryBuilder)
$value = $queryBuilder->escapeValue($value);
if(strcasecmp($value, self::KEY_NULL) == 0)
{
$wheres[] = $columnName . " is null";
$wheres[] = $columnName . self::IS_NULL;
}
else
{
Expand All @@ -940,7 +943,7 @@ private function getWhere($info, $queryBuilder)
{
throw new NoPrimaryKeyDefinedException("No primary key defined");
}
return implode(" and ", $wheres);
return implode(self::CLAUSE_AND, $wheres);
}

/**
Expand Down Expand Up @@ -977,7 +980,7 @@ private function getWhereWithColumns($info, $queryBuilder)
$escapedValue = $queryBuilder->escapeValue($value);
if(strcasecmp($escapedValue, self::KEY_NULL) == 0)
{
$wheres[] = $columnName . " is null";
$wheres[] = $columnName . self::IS_NULL;
$columns[$columnName] = null;
}
else
Expand All @@ -992,7 +995,7 @@ private function getWhereWithColumns($info, $queryBuilder)
}

$result->columns = $columns;
$result->whereClause = implode(" and ", $wheres);
$result->whereClause = implode(self::CLAUSE_AND, $wheres);
return $result;
}

Expand Down Expand Up @@ -2150,14 +2153,14 @@ private function createWhereByPrimaryKeys($queryBuilder, $primaryKeys, $property
$columnValue = $propertyValues[$index];
if($columnValue === null)
{
$wheres[] = $columnName . " is null";
$wheres[] = $columnName . self::IS_NULL;
}
else
{
$wheres[] = $columnName . " = " . $queryBuilder->escapeValue($propertyValues[$index]);
}
}
$where = implode(" and ", $wheres);
$where = implode(self::CLAUSE_AND, $wheres);
if(!$this->isValidFilter($where))
{
throw new InvalidFilterException(self::MESSAGE_INVALID_FILTER);
Expand Down
4 changes: 1 addition & 3 deletions src/Database/PicoPageData.php
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ public function getResultAsArray()
* @param mixed $data The data to convert. Can be a MagicObject, an array, an object, or a scalar value.
* @return mixed The converted data as a plain PHP array, stdClass object, or scalar value.
*/
protected function magicObjectToArray($data)
protected function magicObjectToArray($data) // NOSONAR
{
// Null or scalar
if (is_null($data) || is_scalar($data)) {
Expand Down Expand Up @@ -363,8 +363,6 @@ protected function magicObjectToArray($data)
return $data;
}



/**
* Get the current page number in the pagination context.
*
Expand Down
14 changes: 7 additions & 7 deletions src/Database/PicoSpecificationFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
*/
class PicoSpecificationFilter
{
const DATA_TYPE_NUMBER = "number";
const DATA_TYPE_STRING = "string";
const DATA_TYPE_BOOLEAN = "boolean";
const DATA_TYPE_ARRAY_NUMBER = "number[]";
const DATA_TYPE_ARRAY_STRING = "string[]";
const DATA_TYPE_NUMBER = "number";
const DATA_TYPE_STRING = "string";
const DATA_TYPE_BOOLEAN = "boolean";
const DATA_TYPE_ARRAY_NUMBER = "number[]";
const DATA_TYPE_ARRAY_STRING = "string[]";
const DATA_TYPE_ARRAY_BOOLEAN = "boolean[]";
const DATA_TYPE_FULLTEXT = "fulltext";
const DATA_TYPE_TEXT_EQUALS = "textequals";
const DATA_TYPE_FULLTEXT = "fulltext";
const DATA_TYPE_TEXT_EQUALS = "textequals";

/**
* The name of the column this filter applies to.
Expand Down
4 changes: 2 additions & 2 deletions src/Database/PicoTableInfoExtended.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
*/
class PicoTableInfoExtended extends PicoTableInfo
{
const NAME = "name"; // Key for the column name
const NAME = "name"; // Key for the column name
const PREV_NAME = "prevColumnName"; // Key for the previous column name
const ELEMENT = "element"; // Key for the element
const ELEMENT = "element"; // Key for the element

/**
* Gets an instance of PicoTableInfoExtended.
Expand Down
79 changes: 57 additions & 22 deletions src/Generator/PicoDatabaseDump.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,18 @@ class PicoDatabaseDump // NOSONAR
protected $columns = array();

/**
* Generates a SQL CREATE TABLE statement based on the provided entity schema.
* * This method detects the database type and utilizes the appropriate utility
* class to format columns, primary keys, and auto-increment constraints.
* It supports MySQL, MariaDB, PostgreSQL, SQLite, and SQL Server.
* Instantiates the appropriate database dump utility based on the database type.
*
* @param array $entity The entity schema containing 'name' and 'columns' (an array of column definitions).
* @param string $databaseType The type of database (e.g., PicoDatabaseType::DATABASE_TYPE_MARIADB).
* @param bool $createIfNotExists Whether to add the "IF NOT EXISTS" clause to the CREATE statement.
* @param bool $dropIfExists Whether to prepend a commented-out "DROP TABLE IF EXISTS" statement.
* @param string $engine The storage engine to use (default is 'InnoDB', primarily for MySQL/MariaDB).
* @param string $charset The character set for the table (default is 'utf8mb4').
* * @return string The generated SQL DDL statement or an empty string if the database type is unsupported.
* This factory method maps specific database engines to their respective
* utility classes (MySQL, PostgreSQL, SQLite, or SQL Server) to handle
* database-specific dumping operations.
*
* @param string $databaseType The type of database (e.g., MySQL, PostgreSQL, SQLite).
* @return PicoDatabaseUtilBase|string Returns an instance of the database utility tool
* or an empty string if the database type is not supported.
*/
public function dumpStructureFromSchema($entity, $databaseType, $createIfNotExists = false, $dropIfExists = false, $engine = 'InnoDB', $charset = 'utf8mb4')
private function getDatabaseDumpTool($databaseType)
{
$tableName = $entity['name'];

// 1. Initialize Tool based on Database Type
switch ($databaseType) {
case PicoDatabaseType::DATABASE_TYPE_MARIADB:
case PicoDatabaseType::DATABASE_TYPE_MYSQL:
Expand All @@ -84,6 +78,29 @@ public function dumpStructureFromSchema($entity, $databaseType, $createIfNotExis
default:
return "";
}
return $tool;
}

/**
* Generates a SQL CREATE TABLE statement based on the provided entity schema.
* * This method detects the database type and utilizes the appropriate utility
* class to format columns, primary keys, and auto-increment constraints.
* It supports MySQL, MariaDB, PostgreSQL, SQLite, and SQL Server.
*
* @param array $entity The entity schema containing 'name' and 'columns' (an array of column definitions).
* @param string $databaseType The type of database (e.g., PicoDatabaseType::DATABASE_TYPE_MARIADB).
* @param bool $createIfNotExists Whether to add the "IF NOT EXISTS" clause to the CREATE statement.
* @param bool $dropIfExists Whether to prepend a commented-out "DROP TABLE IF EXISTS" statement.
* @param string $engine The storage engine to use (default is 'InnoDB', primarily for MySQL/MariaDB).
* @param string $charset The character set for the table (default is 'utf8mb4').
* @return string The generated SQL DDL statement or an empty string if the database type is unsupported.
*/
public function dumpStructureFromSchema($entity, $databaseType, $createIfNotExists = false, $dropIfExists = false, $engine = 'InnoDB', $charset = 'utf8mb4')
{
$tableName = $entity['name'];

// 1. Initialize Tool based on Database Type
$tool = $this->getDatabaseDumpTool($databaseType);

$columns = array();
$primaryKeys = array();
Expand Down Expand Up @@ -154,15 +171,11 @@ public function dumpDataFromSchema($entity, $databaseType, $batchSize = 100)
{
// Check if the target database is PostgreSQL
$isPgSql = $databaseType == PicoDatabaseType::DATABASE_TYPE_PGSQL || $databaseType == PicoDatabaseType::DATABASE_TYPE_POSTGRESQL;
$columnInfo = array();

$tableName = $entity['name'];

// 1. Prepare Column Information for type-casting
if (isset($entity['columns']) && is_array($entity['columns'])) {
foreach ($entity['columns'] as $column) {
$columnInfo[$column['name']] = $this->getColumnInfo($column);
}
}
$columnInfo = $this->prepareColumnInfo($entity);

$validColumnNames = array_keys($columnInfo);

Expand Down Expand Up @@ -203,12 +216,34 @@ public function dumpDataFromSchema($entity, $databaseType, $batchSize = 100)
. implode(",\r\n", $rows)
. ";\r\n\r\n";
}

}

return $allSql;
}

/**
* Prepares and normalizes column metadata from the entity schema.
*
* This method iterates through the column definitions of an entity and
* transforms them into a structured associative array of column information,
* indexed by the column names.
*
* @param array $entity The entity schema containing the 'columns' definition.
* @return array<string, \stdClass> An associative array where keys are column names
* and values are column metadata objects.
*/
private function prepareColumnInfo($entity)
{
$columnInfo = array();
if (isset($entity['columns']) && is_array($entity['columns'])) {
foreach ($entity['columns'] as $column) {
// Assuming getColumnInfo returns an object/stdClass based on previous code
$columnInfo[$column['name']] = $this->getColumnInfo($column);
}
}
return $columnInfo;
}

/**
* Formats raw data values based on column metadata and database requirements.
*
Expand Down
Loading