diff --git a/lib/Controller/PublicationsController.php b/lib/Controller/PublicationsController.php index 556ce226..b4022286 100644 --- a/lib/Controller/PublicationsController.php +++ b/lib/Controller/PublicationsController.php @@ -400,14 +400,22 @@ public function createPublicationFile(ObjectService $objectService, ?array $publ } // Initialize Twig - $loader = new FilesystemLoader('lib/Templates'); + $loader = new FilesystemLoader('lib/Templates', '/var/www/html/apps-extra/opencatalogi'); $twig = new Environment($loader); // Render the Twig template $html = $twig->render('publication.html.twig', ['publication' => $publication]); + // Check if the directory exists, if not, create it + if (!file_exists('/tmp/mpdf')) { + mkdir('/tmp/mpdf', 0777, true); + } + + // Set permissions for the directory (ensure it's writable) + chmod('/tmp/mpdf', 0777); + // Initialize mPDF - $mpdf = new Mpdf(); + $mpdf = new Mpdf(['tempDir' => '/tmp/mpdf']); // Write HTML to PDF $mpdf->WriteHTML($html); diff --git a/lib/Db/ListingMapper.php b/lib/Db/ListingMapper.php index fb04d79d..fe665861 100644 --- a/lib/Db/ListingMapper.php +++ b/lib/Db/ListingMapper.php @@ -21,15 +21,15 @@ public function find(int $id): Listing $qb = $this->db->getQueryBuilder(); $qb->select( - 'l.*', - 'o.id AS organisation_id', - 'o.title AS organisation_title', + 'l.*', + 'o.id AS organisation_id', + 'o.title AS organisation_title', 'o.summary AS organisation_summary', 'o.description AS organisation_description', - 'o.image AS organisation_image', - 'o.oin AS organisation_oin', - 'o.tooi AS organisation_tooi', - 'o.rsin AS organisation_rsin', + 'o.image AS organisation_image', + 'o.oin AS organisation_oin', + 'o.tooi AS organisation_tooi', + 'o.rsin AS organisation_rsin', 'o.pki AS organisation_pki' ) ->from('listings', 'l') @@ -42,7 +42,7 @@ public function find(int $id): Listing } /** - * Returns an db result and throws exceptions when there are more or less + * Returns a db result and throws exceptions when there are more or less * results CUSTOM FOR JOINS * * @param IQueryBuilder $query @@ -117,17 +117,17 @@ protected function findEntitiesCustom(IQueryBuilder $query): array { public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array { $qb = $this->db->getQueryBuilder(); - + $qb->select( - 'l.*', - 'o.id AS organisation_id', - 'o.title AS organisation_title', + 'l.*', + 'o.id AS organisation_id', + 'o.title AS organisation_title', 'o.summary AS organisation_summary', 'o.description AS organisation_description', - 'o.image AS organisation_image', - 'o.oin AS organisation_oin', - 'o.tooi AS organisation_tooi', - 'o.rsin AS organisation_rsin', + 'o.image AS organisation_image', + 'o.oin AS organisation_oin', + 'o.tooi AS organisation_tooi', + 'o.rsin AS organisation_rsin', 'o.pki AS organisation_pki' ) ->from('listings', 'l') @@ -135,7 +135,7 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters ->setMaxResults($limit) ->setFirstResult($offset); - + // Apply filters foreach ($filters as $filter => $value) { if ($value === 'IS NOT NULL') { @@ -146,7 +146,7 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters $qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value))); } } - + // Apply search conditions if (!empty($searchConditions)) { $qb->andWhere('(' . implode(' OR ', $searchConditions) . ')'); @@ -154,7 +154,7 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters $qb->setParameter($param, $value); } } - + // Use the existing findEntities method to fetch and map the results return $this->findEntitiesCustom($qb); } diff --git a/lib/Db/PublicationMapper.php b/lib/Db/PublicationMapper.php index 9aa60c71..70d4e72b 100644 --- a/lib/Db/PublicationMapper.php +++ b/lib/Db/PublicationMapper.php @@ -19,13 +19,105 @@ public function find(int $id): Publication { $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from('publications') + $qb->select( + 'p.*', + 'c.id AS catalogi_id', + 'c.title AS catalogi_title', + 'm.id AS metadata_id', + 'm.title AS metadata_title' + ) + ->from('publications', 'p') + ->leftJoin('p', 'catalogi', 'c', 'p.catalogi = c.id') + ->leftJoin('p', 'metadata', 'm', 'p.metaData = m.id') ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) + $qb->expr()->eq('p.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) ); - return $this->findEntity(query: $qb); + return $this->findEntityCustom(query: $qb); + } + + /** + * Returns a db result and throws exceptions when there are more or less + * results CUSTOM FOR JOINS + * + * @param IQueryBuilder $query + * @return Entity the entity + * @psalm-return T the entity + * @throws Exception + * @throws MultipleObjectsReturnedException if more than one item exist + * @throws DoesNotExistException if the item does not exist + * @since 14.0.0 + */ + protected function findEntityCustom(IQueryBuilder $query): Entity { + return $this->mapRowToEntityCustom($this->findOneQuery($query)); + } + + /** + * CUSTOM FOR JOINS + */ + protected function mapRowToEntityCustom(array $row): Entity { + unset($row['DOCTRINE_ROWNUM']); // remove doctrine/dbal helper column + + // Map the Catalogi fields to a sub-array + $catalogiData = [ + 'id' => $row['catalogi_id'] ?? null, + 'title' => $row['catalogi_title'] ?? null, + ]; + + $catalogiIsEmpty = true; + foreach ($catalogiData as $key => $value) { + if ($value !== null) { + $catalogiIsEmpty = false; + } + + if (array_key_exists("catalogi_$key", $row) === true) { + unset($row["catalogi_$key"]); + } + } + + // Map the MetaData fields to a sub-array + $metaDataData = [ + 'id' => $row['metadata_id'] ?? null, + 'title' => $row['metadata_title'] ?? null, + ]; + + $metaDataIsEmpty = true; + foreach ($metaDataData as $key => $value) { + if ($value !== null) { + $metaDataIsEmpty = false; + } + + if (array_key_exists("metadata_$key", $row) === true) { + unset($row["metadata_$key"]); + } + } + + $row['catalogi'] = $catalogiIsEmpty === true ? null : json_encode(Catalog::fromRow($catalogiData)->jsonSerialize()); + $row['metadata'] = $catalogiIsEmpty === true ? null : json_encode(MetaData::fromRow($metaDataData)->jsonSerialize()); + + return \call_user_func($this->entityClass .'::fromRow', $row); + } + + /** + * Runs a sql query and returns an array of entities CUSTOM FOR JOINS + * + * @param IQueryBuilder $query + * @return Entity[] all fetched entities + * @psalm-return T[] all fetched entities + * @throws Exception + * @since 14.0.0 + */ + protected function findEntitiesCustom(IQueryBuilder $query): array { + $result = $query->executeQuery(); + try { + $entities = []; + while ($row = $result->fetch()) { + $entities[] = $this->mapRowToEntityCustom($row); + } + return $entities; + } finally { + $result->closeCursor(); + } } private function parseComplexFilter(IQueryBuilder $queryBuilder, array $filter, string $name): IQueryBuilder @@ -102,8 +194,16 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters { $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from('publications') + $qb->select( + 'p.*', + 'c.id AS catalogi_id', + 'c.title AS catalogi_title', + 'm.id AS metadata_id', + 'm.title AS metadata_title' + ) + ->from('publications', 'p') + ->leftJoin('p', 'catalogi', 'c', 'p.catalogi = c.id') + ->leftJoin('p', 'metadata', 'm', 'p.metaData = m.id') ->setMaxResults($limit) ->setFirstResult($offset); @@ -123,7 +223,8 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters } } - return $this->findEntities(query: $qb); + // Use the existing findEntities method to fetch and map the results + return $this->findEntitiesCustom($qb); } public function createFromArray(array $object): Publication diff --git a/lib/Templates/publication.html.twig b/lib/Templates/publication.html.twig index dd95ce3a..983effbf 100644 --- a/lib/Templates/publication.html.twig +++ b/lib/Templates/publication.html.twig @@ -1,15 +1,15 @@ -

Publicatie #{{ publicatie.title }}

-

Catalogi: {{ publicatie.catalogi.title }}

-

Publicatie Type: {{ publicatie.metadata.title }}

-

Referentie: {{ publicatie.reference }}

-

Samenvatting: {{ publicatie.summary }}

-

Beschrijving: {{ publicatie.description }}

+

Publicatie #{{ publication.title }}

+{#

Catalogi: {{ publication.catalogi.title }}

#} +{#

Publicatie Type: {{ publication.metadata.title }}

#} +

Referentie: {{ publication.reference }}

+

Samenvatting: {{ publication.summary }}

+

Beschrijving: {{ publication.description }}

- {% for key, value in publicatie.data %} + {% for key, value in publication.data %} diff --git a/src/entities/publication/publication.mock.ts b/src/entities/publication/publication.mock.ts index 2c819d4f..62d46fa4 100644 --- a/src/entities/publication/publication.mock.ts +++ b/src/entities/publication/publication.mock.ts @@ -11,8 +11,71 @@ export const mockPublicationsData = (): TPublication[] => [ image: 'https://example.com/image.jpg', category: 'category1', portal: 'https://google.com', - catalogi: 'catalogi1', - metaData: 'meta1', + catalogi: { // full data + id: '1', + title: 'Decat', + summary: 'a short form summary', + description: 'a really really long description about this catalogus', + image: 'string', + listed: false, + organisation: '23', + metadata: ['1', '3'], + }, + metaData: { // full data + id: '1', + title: 'Test metadata', + description: 'this is a very long description for test metadata', + version: '0.0.1', + required: ['test'], + properties: { + test: { + title: 'test prop', + description: 'a long description', + type: 'string', + format: 'date', + pattern: 1, + default: 'true', + behavior: 'silly', + required: false, + deprecated: false, + minLength: 5, + maxLength: 6, + example: 'gooby example', + minimum: 1, + maximum: 3, + multipleOf: 1, + exclusiveMin: false, + exclusiveMax: false, + minItems: 0, + maxItems: 6, + }, + gfdgds: { + title: 'gfdgds prop', + description: 'property description', + type: 'string', + format: 'uuid', + pattern: 2, + default: 'false', + behavior: 'goofy perchance', + required: false, + deprecated: false, + minLength: 5.5, + maxLength: 5.11, + example: 'bazinga', + minimum: 1, + maximum: 2, + multipleOf: 1, + exclusiveMin: true, + exclusiveMax: false, + minItems: 1, + maxItems: 7, + }, + }, + archive: { + valuation: 'b', + class: 1, + }, + }, published: '2024-01-01', modified: '2024-01-02', featured: true, @@ -94,8 +157,72 @@ export const mockPublicationsData = (): TPublication[] => [ image: 'https://example.com/image.jpg', category: 'category3', portal: 'https://google.com', - catalogi: 'catalogi3', - metaData: 'meta1', + catalogi: { // full data + id: '3', + title: 'Foo', + summary: 'a short form summary', + description: 'a really really long description about this catalogus', + image: 'string', + // @ts-expect-error -- listed needs to be a boolean + listed: 0.2, + organisation: '23', + metadata: ['1', '3'], + }, + metaData: { // full data + id: '1', + title: 'Test metadata', + description: 'this is a very long description for test metadata', + version: '0.0.1', + required: ['test'], + properties: { + test: { + title: 'test prop', + description: 'a long description', + type: 'string', + format: 'date', + pattern: 1, + default: 'true', + behavior: 'silly', + required: false, + deprecated: false, + minLength: 5, + maxLength: 6, + example: 'gooby example', + minimum: 1, + maximum: 3, + multipleOf: 1, + exclusiveMin: false, + exclusiveMax: false, + minItems: 0, + maxItems: 6, + }, + gfdgds: { + title: 'gfdgds prop', + description: 'property description', + type: 'string', + format: 'uuid', + pattern: 2, + default: 'false', + behavior: 'goofy perchance', + required: false, + deprecated: false, + minLength: 5.5, + maxLength: 5.11, + example: 'bazinga', + minimum: 1, + maximum: 2, + multipleOf: 1, + exclusiveMin: true, + exclusiveMax: false, + minItems: 1, + maxItems: 7, + }, + }, + archive: { + valuation: 'b', + class: 1, + }, + }, published: '2024-01-01', modified: '2024-01-02', featured: true, diff --git a/src/entities/publication/publication.ts b/src/entities/publication/publication.ts index 86d06763..41628770 100644 --- a/src/entities/publication/publication.ts +++ b/src/entities/publication/publication.ts @@ -1,4 +1,6 @@ import { TAttachment } from '../' +import { TCatalogi } from '../' +import { TMetadata } from '../' import { TPublication } from './publication.types' import { SafeParseReturnType, z } from 'zod' @@ -42,8 +44,8 @@ export class Publication implements TPublication { coordinates: [number, number] } - public catalogi: string - public metaData: string + public catalogi: string|TCatalogi + public metaData: string|TMetadata constructor(data: TPublication) { this.hydrate(data) diff --git a/src/entities/publication/publication.types.ts b/src/entities/publication/publication.types.ts index cd3174ac..92540e81 100644 --- a/src/entities/publication/publication.types.ts +++ b/src/entities/publication/publication.types.ts @@ -1,6 +1,8 @@ // TODO: double check this type for correct properties and optionals when stoplight updates - https://conduction.stoplight.io/docs/open-catalogi/fee989a9c8e3f-publication -import { TAttachment } from '../attachment' +import { TAttachment } from '../' +import { TCatalogi } from '../' +import { TMetadata } from '../' export type TPublication = { id: string @@ -36,6 +38,6 @@ export type TPublication = { type: 'Point' | 'LineString' | 'Polygon' | 'MultiPoint' | 'MultiLineString' | 'MultiPolygon' coordinates: [number, number] } - catalogi: string - metaData: string + catalogi: string|TCatalogi + metaData: string|TMetadata }
Key Value
{{ key }} {{ value }}