File indexing completed on 2024-12-15 03:45:05

0001 <?php
0002 /*
0003     SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
0004 
0005     SPDX-License-Identifier: MIT
0006 */
0007 
0008 require_once('aggregation.php');
0009 require_once('schemaentry.php');
0010 require_once('utils.php');
0011 
0012 /** Represents a product and its schema. */
0013 class Product
0014 {
0015     public $name;
0016     public $schema = array();
0017     public $aggregation = array();
0018 
0019     private $productId = -1;
0020 
0021     /** Name of the primary data table for this product, ie.
0022      *  the one containing all scalar data.
0023      */
0024     public function dataTableName()
0025     {
0026         $tableName = 'pd_' . Utils::normalizeString($this->name);
0027         return strtolower($tableName);
0028     }
0029 
0030     /** Returns the numeric database id of this product for use in queries. */
0031     public function id()
0032     {
0033         return $this->productId;
0034     }
0035 
0036     /** Retrieve all products from storage. */
0037     public static function allProducts(Datastore $db)
0038     {
0039         $products = array();
0040         $stmt = $db->prepare('SELECT col_id, col_name FROM tbl_product');
0041         $db->execute($stmt);
0042         foreach ($stmt as $row) {
0043             $p = new Product();
0044             $p->productId = intval($row['col_id']);
0045             $p->name = strval($row['col_name']);
0046             $p->schema = SchemaEntry::loadSchema($db, $p);
0047             $p->aggregation = Aggregation::aggregationsForProduct($db, $p);
0048             array_push($products, $p);
0049         }
0050         return $products;
0051     }
0052 
0053     /** Retrieve a specific product by name from storage. */
0054     public static function productByName(Datastore $db, $name)
0055     {
0056         if (!is_string($name) || strlen($name) <= 0)
0057             throw new RESTException('Invalid product name.', 400);
0058 
0059         $stmt = $db->prepare('SELECT col_id, col_name FROM tbl_product WHERE col_name = :name');
0060         $stmt->bindValue(':name', strval($name), PDO::PARAM_STR);
0061         $db->execute($stmt);
0062         foreach ($stmt as $row) {
0063             $p = new Product();
0064             $p->productId = intval($row['col_id']);
0065             $p->name = strval($row['col_name']);
0066             $p->schema = SchemaEntry::loadSchema($db, $p);
0067             return $p;
0068         }
0069         return null;
0070     }
0071 
0072     /** Insert new product into database. */
0073     public function insert(Datastore $db)
0074     {
0075         // create product entry
0076         $stmt = $db->prepare('INSERT INTO tbl_product (col_name) VALUES (:name)');
0077         $stmt->bindValue(':name', $this->name, PDO::PARAM_STR);
0078         $db->execute($stmt);
0079         $this->productId = $db->pdoHandle()->lastInsertId();
0080 
0081         // create data tables;
0082         $stmt = $db->prepare('CREATE TABLE ' . $this->dataTableName() . ' (' .
0083             Utils::primaryKeyColumnDeclaration($db->driver(), 'col_id') .
0084             ', col_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP)
0085         ');
0086         $db->execute($stmt);
0087 
0088         // create schema entries
0089         foreach ($this->schema as $entry)
0090             $entry->insert($db, $this->productId);
0091 
0092         // store aggregation settings
0093         Aggregation::update($db, $this, $this->aggregation);
0094     }
0095 
0096     /** Update an existing product in the database to match @p $newProduct. */
0097     public function update(Datastore $db, Product $newProduct)
0098     {
0099         $oldSchema = array();
0100         foreach ($this->schema as $oldEntry)
0101             $oldSchema[$oldEntry->name] = $oldEntry;
0102 
0103         foreach ($newProduct->schema as $newEntry) {
0104             if (array_key_exists($newEntry->name, $oldSchema)) {
0105                 // update
0106                 $oldEntry = $oldSchema[$newEntry->name];
0107                 $oldEntry->update($db, $newEntry);
0108             } else {
0109                 // insert
0110                 $newEntry->insert($db, $this->productId);
0111             }
0112             unset($oldSchema[$newEntry->name]);
0113         }
0114 
0115         // delete whatever is left
0116         foreach($oldSchema as $entry)
0117             $entry->delete($db, $this->productId);
0118 
0119         // store aggregation settings
0120         Aggregation::update($db, $this, $newProduct->aggregation);
0121     }
0122 
0123     /** Delete an existing product in the database. */
0124     public function delete(Datastore $db)
0125     {
0126         // delete aggregation settings
0127         Aggregation::delete($db, $this);
0128 
0129         // delete schema entries
0130         foreach ($this->schema as $entry)
0131             $entry->delete($db, $this->productId);
0132 
0133         // delete data tables
0134         $stmt = $db->prepare('DROP TABLE ' . $this->dataTableName() . ($db->driver() == 'sqlite' ? '' : ' CASCADE'));
0135         $db->execute($stmt);
0136 
0137         // delete product
0138         $stmt = $db->prepare('DELETE FROM tbl_product WHERE col_id = :id');
0139         $stmt->bindValue(':id', $this->productId, PDO::PARAM_INT);
0140         $db->execute($stmt);
0141     }
0142 
0143     /** Create one Product instance based on JSON input and verifies it is valid. */
0144     public static function fromJson($jsonString)
0145     {
0146         $jsonObj = json_decode($jsonString);
0147         if (!property_exists($jsonObj, 'name'))
0148             throw new RESTException('No product name specified.', 400);
0149 
0150         $p = new Product();
0151         $p->name = $jsonObj->name;
0152         $p->schema = SchemaEntry::fromJson($jsonObj->schema, $p);
0153         if (property_exists($jsonObj, 'aggregation'))
0154             $p->aggregation = Aggregation::fromJson($jsonObj->aggregation);
0155 
0156         // verify
0157         if (strlen($p->name) <= 0 || !is_string($p->name))
0158             throw new RESTException('Empty product name.', 400);
0159         if (!ctype_alpha($p->name[0]))
0160             throw new RESTException("Invalid product name, must start with a letter.", 400);
0161 
0162         return $p;
0163     }
0164 }
0165 
0166 ?>