Browse Source

fix generate index name function and improve index metadata

michelphp 1 week ago
parent
commit
e8702fe247

+ 40 - 0
src/Cache/IndexCache.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Michel\PaperORM\Cache;
+
+
+use Michel\PaperORM\Mapping\Column\Column;
+use Michel\PaperORM\Mapping\Index;
+
+final class IndexCache
+{
+    private static ?IndexCache $instance = null;
+    private array $data = [];
+
+    public static function getInstance(): self
+    {
+        if (self::$instance === null) {
+            self::$instance = new self();
+        }
+        return self::$instance;
+    }
+    
+    public function set(string $key, array $indexes)
+    {
+        foreach ($indexes as $index) {
+            if (!$index instanceof Index) {
+                throw new \InvalidArgumentException(sprintf('All values in the array must be instances of %s.', Index::class));
+            }
+        }
+
+        $this->data[$key] = $indexes;
+    }
+
+    public function get(string $key): array
+    {
+        if (isset($this->data[$key])) {
+            return $this->data[$key];
+        }
+        return [];
+    }
+}

+ 7 - 0
src/Entity/TableMetadataInterface.php

@@ -2,9 +2,16 @@
 
 namespace Michel\PaperORM\Entity;
 
+use Michel\PaperORM\Mapping\Index;
+
 interface TableMetadataInterface
 {
     static public function getTableName(): string;
+
+    /**
+     * @return array<Index>
+     */
+    static public function getIndexes(): array;
     static public function getRepositoryName(): ?string;
     static public function columnsMapping(): array;
 }

+ 82 - 0
src/Mapper/IndexMapper.php

@@ -0,0 +1,82 @@
+<?php
+
+namespace Michel\PaperORM\Mapper;
+
+
+use Michel\PaperORM\Cache\ColumnCache;
+use Michel\PaperORM\Cache\IndexCache;
+use Michel\PaperORM\Cache\OneToManyCache;
+use Michel\PaperORM\Entity\EntityInterface;
+use Michel\PaperORM\Entity\TableMetadataInterface;
+use Michel\PaperORM\Mapping\Entity;
+use Michel\PaperORM\Mapping\Index;
+use Michel\PaperORM\Proxy\ProxyInterface;
+
+final class IndexMapper
+{
+    /**
+     * @param $class
+     * @return array<Index>
+     */
+    static public function getIndexes($class): array
+    {
+        $cache = IndexCache::getInstance();
+        $key = is_object($class) ? get_class($class) : $class;
+        $indexes = $cache->get($key);
+        if (!empty($indexes)) {
+            return $indexes;
+        }
+        $indexes = self::getIndexesMapping($class);
+        $cache->set($key, $indexes);
+        return $indexes;
+    }
+
+    static public function getIndexesMapping($class): array
+    {
+        if (!is_subclass_of($class, EntityInterface::class)) {
+            throw new \LogicException(sprintf('%s must implement %s', $class, EntityInterface::class));
+        }
+
+        if (is_subclass_of($class, TableMetadataInterface::class)) {
+            return $class::getIndexes();
+        }
+
+        if (PHP_VERSION_ID >= 80000) {
+            $indexes = self::getIndexesPHP8($class);
+            if (!empty($indexes)) {
+                return $indexes;
+            }
+        }
+
+        if (method_exists($class, 'getIndexes')) {
+            return $class::getIndexes();
+        }
+
+        throw new \LogicException(sprintf(
+            'Entity %s must define a Index via interface, attribute or static method',
+            is_object($class) ? get_class($class) : $class
+        ));
+    }
+
+    static private function getIndexesPHP8($class): array
+    {
+        if ($class instanceof ProxyInterface) {
+            $class = $class->__getParentClass();
+        }elseif (is_subclass_of($class, ProxyInterface::class)) {
+            $reflector = new \ReflectionClass($class);
+            $parentClass = $reflector->getParentClass();
+            if ($parentClass) {
+                $class = $parentClass->getName();
+            }
+        }
+
+        $reflector = new \ReflectionClass($class);
+        $attributes = $reflector->getAttributes(Index::class);
+        $indexes = [];
+        foreach ($attributes as $attribute) {
+            $indexes[] = $attribute->newInstance();
+        }
+        return $indexes;
+    }
+
+}

+ 1 - 1
src/Mapping/Index.php

@@ -2,7 +2,7 @@
 
 namespace Michel\PaperORM\Mapping;
 
-#[\Attribute(\Attribute::TARGET_CLASS)]
+#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)]
 final class Index
 {
     private array $columns;

+ 59 - 61
src/Metadata/IndexMetadata.php

@@ -1,61 +1,59 @@
-<?php
-
-namespace Michel\PaperORM\Metadata;
-
-use Michel\PaperORM\Mapping\Column\JoinColumn;
-
-final class IndexMetadata
-{
-    private string $tableName;
-    private ?string $name;
-    private array $columns;
-    private bool $unique;
-
-    public function __construct(string $tableName, ?string $name, array $columns, bool $unique = false)
-    {
-        $this->tableName = $tableName;
-        $this->name = strtoupper($name);
-        $this->columns = $columns;
-        $this->unique = $unique;
-    }
-
-    public function getTableName(): string
-    {
-        return $this->tableName;
-    }
-
-    public function getName(): ?string
-    {
-        return $this->name;
-    }
-
-    public function getColumns(): array
-    {
-        return $this->columns;
-    }
-
-    public function isUnique(): bool
-    {
-        return $this->unique;
-    }
-
-    public static function fromArray(array $data): self
-    {
-        return new self(
-            $data['tableName'],
-            $data['name'],
-            $data['columns'],
-            $data['unique']
-        );
-    }
-
-    public function toArray(): array
-    {
-        return [
-            'tableName' => $this->getTableName(),
-            'name' => $this->getName(),
-            'columns' => $this->getColumns(),
-            'unique' => $this->isUnique()
-        ];
-    }
-}
+<?php
+
+namespace Michel\PaperORM\Metadata;
+
+final class IndexMetadata
+{
+    private string $tableName;
+    private ?string $name;
+    private array $columns;
+    private bool $unique;
+
+    public function __construct(string $tableName, ?string $name, array $columns, bool $unique = false)
+    {
+        $this->tableName = $tableName;
+        $this->name = strtoupper($name);
+        $this->columns = $columns;
+        $this->unique = $unique;
+    }
+
+    public function getTableName(): string
+    {
+        return $this->tableName;
+    }
+
+    public function getName(): ?string
+    {
+        return $this->name;
+    }
+
+    public function getColumns(): array
+    {
+        return $this->columns;
+    }
+
+    public function isUnique(): bool
+    {
+        return $this->unique;
+    }
+
+    public static function fromArray(array $data): self
+    {
+        return new self(
+            $data['tableName'],
+            $data['name'],
+            $data['columns'],
+            $data['unique']
+        );
+    }
+
+    public function toArray(): array
+    {
+        return [
+            'tableName' => $this->getTableName(),
+            'name' => $this->getName(),
+            'columns' => $this->getColumns(),
+            'unique' => $this->isUnique()
+        ];
+    }
+}

+ 2 - 1
src/Migration/PaperMigration.php

@@ -3,6 +3,7 @@
 namespace Michel\PaperORM\Migration;
 
 use DateTime;
+use Michel\PaperORM\Mapper\IndexMapper;
 use PDOException;
 use Michel\PaperORM\Entity\EntityInterface;
 use Michel\PaperORM\EntityManagerInterface;
@@ -300,7 +301,7 @@ SQL;
                 $tableName = EntityMapper::getTable($entity);
                 $tables[$tableName] = [
                     'columns' => ColumnMapper::getColumns($entity),
-                    'indexes' => [] // TODO IndexMapper::getIndexes($entity)
+                    'indexes' => IndexMapper::getIndexes($entity)
                 ];
             }
         }

+ 4 - 5
src/Platform/AbstractPlatform.php

@@ -236,12 +236,11 @@ abstract class AbstractPlatform implements PlatformInterface
 
     final protected function generateIndexName(string $tableName, array $columnNames, bool $unique): string
     {
-        $hash = implode('', array_map(static function ($column) {
-            return dechex(crc32($column));
-        }, array_merge([$tableName], $columnNames)));
-
+        $signature = $tableName . implode('_', $columnNames);
+        $hash = hash('sha256', $signature);
         $prefix = $unique ? $this->getPrefixUniqIndexName() : $this->getPrefixIndexName();
-        return strtoupper(substr($prefix . $hash, 0, $this->getMaxLength()));
+
+        return strtoupper($prefix . substr($hash, 0, $this->getMaxLength() - strlen($prefix)));
     }
 
     final protected function generateForeignKeyName(string $tableName, array $columnNames): string

+ 5 - 0
tests/Entity/CommentTest.php

@@ -82,4 +82,9 @@ class CommentTest implements EntityInterface, TableMetadataInterface
         $this->post = $post;
         return $this;
     }
+
+    static public function getIndexes(): array
+    {
+        return [];
+    }
 }