UNPKG

entity-baker

Version:

Generates simple and powerful entity classes for ORM systems, like Doctrine and/or Entity Framework.

612 lines (584 loc) 21.2 kB
"use strict"; /** * This file is part of the node-entity-baker distribution. * Copyright (c) Marcel Joachim Kloubert. * * node-entity-baker is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, version 3. * * node-entity-baker is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const eb_lib_compiler = require("./compiler"); const eb_lib_helpers = require("./helpers"); const Enumerable = require("node-enumerable"); const FSExtra = require("fs-extra"); const Path = require("path"); /** * Generates a class for Doctrine. * * @param {GenerateClassContext} context */ function generateClassForDoctrine(context) { return __awaiter(this, void 0, void 0, function* () { const ME = this; const CLASS_NAME = context.name; const PHP_NAMESPACE = context['namespace'].join("\\"); const TRAIT_NAME = CLASS_NAME; const TRAIT_NAMESPACE = context['namespace'].map(n => n) .concat(['Extensions']) .join("\\"); const PHP_FULL_CLASS_NAME = "\\" + PHP_NAMESPACE + ('' === PHP_NAMESPACE ? '' : "\\") + CLASS_NAME; const IS_AUTO = (col) => { return eb_lib_helpers.toBooleanSafe(context.columns[col].auto); }; const IS_ID = (col) => { return eb_lib_helpers.toBooleanSafe(context.columns[col].id); }; const TO_DOCTRINE_TYPE = (col) => { let type = eb_lib_helpers.normalizeString(context.columns[col].type); switch (type) { case eb_lib_compiler.TYPE_BIGINT: case eb_lib_compiler.TYPE_INT64: case eb_lib_compiler.TYPE_UINT64: type = 'bigint'; break; case eb_lib_compiler.TYPE_BIN: case eb_lib_compiler.TYPE_BINARY: type = 'binary'; break; case eb_lib_compiler.TYPE_BLOB: type = 'blob'; break; case eb_lib_compiler.TYPE_BOOL: case eb_lib_compiler.TYPE_BOOLEAN: type = 'boolean'; break; case eb_lib_compiler.TYPE_DATE: type = 'date'; break; case eb_lib_compiler.TYPE_DATETIME: type = 'datetime'; break; case eb_lib_compiler.TYPE_DATETIME_TZ: type = 'datetimetz'; break; case eb_lib_compiler.TYPE_DECIMAL: type = 'decimal'; break; case eb_lib_compiler.TYPE_FLOAT: type = 'float'; break; case eb_lib_compiler.TYPE_GUID: case eb_lib_compiler.TYPE_UUID: type = 'guid'; break; case eb_lib_compiler.TYPE_INT: case eb_lib_compiler.TYPE_INT32: case eb_lib_compiler.TYPE_INTEGER: case eb_lib_compiler.TYPE_UINT32: type = 'integer'; break; case eb_lib_compiler.TYPE_INT16: case eb_lib_compiler.TYPE_SMALLINT: case eb_lib_compiler.TYPE_UINT16: type = 'smallint'; break; case eb_lib_compiler.TYPE_JSON: type = 'json'; break; case eb_lib_compiler.TYPE_STR: case eb_lib_compiler.TYPE_STRING: break; case eb_lib_compiler.TYPE_TEXT: type = 'text'; break; case eb_lib_compiler.TYPE_TIME: type = 'time'; break; case eb_lib_compiler.TYPE__DEFAULT: type = 'string'; if (IS_ID(col)) { type = 'integer'; } break; default: throw new Error(`The data type '${type}' is not supported by Doctrine!`); } return type; }; let dbTable = eb_lib_helpers.toStringSafe(context.entity.table).trim(); if ('' === dbTable) { dbTable = CLASS_NAME; } let classFile = ''; classFile += `<?php /** * AUTO GENERATED FILE * * Generated by node-entity-baker (https://www.npmjs.com/package/entity-baker) **/ `; let outDir = context.outDir; if (context['namespace'].length > 0) { for (const NS of context['namespace']) { outDir = Path.join(outDir, NS); } classFile += `namespace ${PHP_NAMESPACE}; `; } outDir = Path.resolve(outDir); if (!(yield eb_lib_helpers.exists(outDir))) { yield FSExtra.mkdirs(outDir); } const CLASS_FILENAME = `${CLASS_NAME}.php`; const CLASS_FILE_PATH = Path.resolve(Path.join(outDir, CLASS_FILENAME)); const EXTENSIONS_DIR = Path.resolve(Path.join(outDir, 'Extensions')); if (!(yield eb_lib_helpers.exists(EXTENSIONS_DIR))) { yield FSExtra.mkdirs(EXTENSIONS_DIR); } const TRAIT_FILENAME = `${TRAIT_NAME}.php`; const TRAIT_FILE_PATH = Path.resolve(Path.join(EXTENSIONS_DIR, TRAIT_FILENAME)); const XML_FILENAME = `${context.namespace.concat(CLASS_NAME).join('.')}.dcm.xml`; let xmlOutDir; if (context.options.doctrine) { xmlOutDir = context.options.doctrine.xmlOutDir; } if (eb_lib_helpers.isEmptyString(xmlOutDir)) { xmlOutDir = context.outDir; } else { if (!Path.isAbsolute(xmlOutDir)) { xmlOutDir = Path.join(context.outDir, xmlOutDir); } } const XML_FILE_PATH = Path.resolve(Path.join(xmlOutDir, XML_FILENAME)); classFile += `/** * @Entity @Table(name="${dbTable}") **/ class ${CLASS_NAME} implements \\ArrayAccess { /** * @see ./Extensions/${TRAIT_FILENAME} */ use Extensions\\${TRAIT_NAME}; /** * Initializes a new instance of the * '${PHP_FULL_CLASS_NAME}' * class. * * @param mixed $arg,... One or more arguments for the object. **/ public function __construct() { // check if we have a // 'onConstructor()' // method in './Extensions/${TRAIT_FILENAME}' if (\\method_exists($this, 'onConstructor')) { \\call_user_func_array( [ $this, 'onConstructor' ], \\func_get_args() ); } } `; for (const C of context.columnNames) { const COLUMN = context.columns[C]; classFile += ` /**${IS_ID(C) ? ' @Id' : ''} @Column(type="${TO_DOCTRINE_TYPE(C)}")${IS_AUTO(C) ? ' @GeneratedValue' : ''} **/ protected $` + C + `;`; } const GETTERS = {}; const SETTERS = {}; for (const C of context.columnNames) { const COLUMN = context.columns[C]; const METHOD_SUFFIX = context.methods[C]; let hasGetter = true; let hasSetter = !IS_AUTO(C); if (hasGetter || hasSetter) { classFile += ` `; } if (hasGetter) { const GETTER_NAME = `get${METHOD_SUFFIX}`; GETTERS[C] = GETTER_NAME; classFile += ` /** * Gets the value of '${C}' column. * * @return ${getPHPDataType(COLUMN.type)} The value of '${C}'. **/ public function ${GETTER_NAME}() { $valueToReturn = $this->${C}; // check if we have a // 'onBeforeGet($columnName, &$valueToReturn)' // method in './Extensions/${TRAIT_FILENAME}' if (\\method_exists($this, 'onBeforeGet')) { $this->onBeforeGet('${C}', $valueToReturn); } return $valueToReturn; }`; } if (hasSetter) { const SETTER_NAME = `set${METHOD_SUFFIX}`; SETTERS[C] = SETTER_NAME; classFile += ` /** * Sets the value for '${C}' column. * * @param ${getPHPDataType(COLUMN.type)} $newValue The new value. * * @return ${PHP_FULL_CLASS_NAME} That instance. * * @chainable **/ public function ${SETTER_NAME}($newValue) { $oldValue = $this->${C}; $doSet = true; // 'onBeforeSet($columnName, &$newValue)' // method in './Extensions/${TRAIT_FILENAME}'? if (\\method_exists($this, 'onBeforeSet')) { $doSet = FALSE !== $this->onBeforeSet('${C}', $newValue, $this->${C}); } if ($doSet) { $this->${C} = $newValue; // 'onSet($columnName, $newValue, $oldValue)' // method in './Extensions/${TRAIT_FILENAME}'? if (\\method_exists($this, 'onSet')) { $this->onSet('${C}', $newValue, $oldValue); } } // 'onSetComplete($columnName, $hasBeenSet, $currentValue, $oldValue, $newValue)' // method in './Extensions/${TRAIT_FILENAME}'? if (\\method_exists($this, 'onSetComplete')) { $this->onSetComplete('${C}', $doSet, $this->${C}, $oldValue, $newValue); } return $this; }`; } } classFile += ` `; // get_getters() { classFile += ` /** * Returns all getters as array. * * @return callable[] The getters. **/ public function get_getters() { return array(`; for (const C of context.columnNames) { const G = GETTERS[C]; if (!G) { continue; } classFile += ` '${C}' => array($this, '${G}'),`; } classFile += ` ); }`; } // get_setters() { classFile += ` /** * Returns all setters as array. * * @return callable[] The setters. **/ public function get_setters() { return array(`; for (const C of context.columnNames) { const S = SETTERS[C]; if (!S) { continue; } classFile += ` '${C}' => array($this, '${S}'),`; } classFile += ` ); }`; } classFile += ` `; // get_columns() { classFile += ` /** * Writes columns and their values to an array (-like object). * * @param array|\\ArrayAccess &$target The target where to write the columns to. * @param string|callable [$columnFilter] An optional filter for the columns as regular expression or callable function. * * @return ${PHP_FULL_CLASS_NAME} That instance. * * @chainable **/ public function copy_columns_to(&$target, $columnFilter = null) { if (null !== $columnFilter) { $columnFilter = (string)$columnFilter; if (!\\is_callable($columnFilter)) { $r = $columnFilter; $columnFilter = function($columnName) use ($r) { return 1 === \\preg_match($r, $columnName); }; } } if (null === $target) { $target = array(); } foreach ($this->get_columns() as $columnName => $columnValue) { $writeColumn = true; if (null !== $columnFilter) { $writeColumn = FALSE !== \\call_user_func_array( $columnFilter, array($columnName, $columnValue) ); } if ($writeColumn) { $target[ $columnName ] = $columnValue; } } return $this; } /** * Returns columns (and their values) as array. * * @return array The columns and their values. **/ public function get_columns() { return array(`; for (const C in context.columns) { const G = GETTERS[C]; if (!G) { continue; } classFile += ` '${C}' => $this->${G}(),`; } classFile += ` ); }`; } // set_columns() { classFile += ` /** * Sets one or more column values at onces. * * @param array|\\Traversable $columnsToSet The columns to set, by using the keys as column names. * * @return ${PHP_FULL_CLASS_NAME} That instance. * * @chainable **/ public function set_columns($columnsToSet) { $setters = $this->get_setters(); if ($columnsToSet) { foreach ($columnsToSet as $columnName => $newValue) { \\call_user_func($setters[ \\trim($columnName) ], $newValue); } } return $this; }`; } classFile += ` /** @inheritdoc **/ public function offsetExists($columnName) { return FALSE !== \\array_search( \\trim($columnName), array(${Object.keys(context.columns).map(k => "'" + k + "'").join(', ')}) ); } /** @inheritdoc **/ public function offsetGet($columnName) { return $this->get_columns()[ \\trim($columnName) ]; } /** @inheritdoc **/ public function offsetSet($columnName, $newValue) { $this->set_columns( array( $columnName => $newValue, ) ); } /** @inheritdoc **/ public function offsetUnset($columnName) { $this->set_columns( array( $columnName => null, ) ); } } `; yield eb_lib_helpers.writeFile(CLASS_FILE_PATH, classFile, 'utf8'); if (!(yield eb_lib_helpers.exists(TRAIT_FILE_PATH))) { let traitFile = ''; traitFile += `<?php `; traitFile += `namespace ${TRAIT_NAMESPACE}; `; traitFile += `/** * Extensions for * '${PHP_FULL_CLASS_NAME}' * class. **/ trait ${TRAIT_NAME} { /** * Optional method for the logic for the constructor of * '${PHP_FULL_CLASS_NAME}' * class. * * This is a default logic. It can be overwritten and can * have another number and kind of parameters has shown here. */ protected function onConstructor($columnsToSet = null) { $this->set_columns($columnsToSet); } /** * An optional method, that is invoked in a getter * BEFORE a value is returned. * * @param string $columnName The name of the column. * @param mixed &$valueToReturn The value to return. **/ protected function onBeforeGet($columnName, &$valueToReturn) { } /** * An optional method, that is invoked in a setter * BEFORE a value is going to be set / changed. * * @param string $columnName The name of the column. * @param mixed &$valueToSet The value to set. * @param mixed $currentValue The current value. * * @return void|false If FALSE, the value will NOT be set. **/ protected function onBeforeSet($columnName, &$valueToSet, $currentValue) { } /** * An optional method, that is invoked in a setter * if a column value has been set / changed. * * @param string $columnName The name of the column. * @param mixed $newValue The new value. * @param mixed $oldValue The old value. **/ protected function onSet($columnName, $newValue, $oldValue) { } /** * An optional method, that is invoked in a setter * after its operation has been finished. * * @param string $columnName The name of the column. * @param boolean $hasBeenSet The value has been set / changed or not. * @param mixed $currentValue The current column value. * @param mixed $oldValue The old value. * @param mixed $newValue The current value of $newValue parameter of setter. **/ protected function onSetComplete($columnName, $hasBeenSet, $currentValue, $oldValue, $newValue) { } `; traitFile += `} `; yield eb_lib_helpers.writeFile(TRAIT_FILE_PATH, traitFile, 'utf8'); } const COLUMNS_FOR_XML = Enumerable.from(context.columnNames).orderBy(cn => { return IS_ID(cn) ? 0 : 1; }).thenBy(cn => { return eb_lib_helpers.normalizeString(cn); }).toArray(); let xmlFile = ''; xmlFile += `<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd"> <entity name="${context.namespace.join('\\')}\\${CLASS_NAME}" table="${dbTable}"> `; for (const C of COLUMNS_FOR_XML) { const COLUMN = context.columns[C]; const TYPE = TO_DOCTRINE_TYPE(C); if (IS_ID(C)) { xmlFile += ` <id name="${C}" type="${TYPE}"`; if (IS_AUTO(C)) { xmlFile += `> <generator strategy="AUTO" /> `; xmlFile += `</id>`; } else { xmlFile += ` />`; } } else { xmlFile += ` <field name="${C}" type="${TYPE}" />`; } } xmlFile += ` </entity> </doctrine-mapping>`; yield eb_lib_helpers.writeFile(XML_FILE_PATH, xmlFile, 'utf8'); }); } exports.generateClassForDoctrine = generateClassForDoctrine; function getPHPDataType(entityType) { switch (eb_lib_helpers.normalizeString(entityType)) { case eb_lib_compiler.TYPE__DEFAULT: case eb_lib_compiler.TYPE_DECIMAL: case eb_lib_compiler.TYPE_GUID: case eb_lib_compiler.TYPE_STR: case eb_lib_compiler.TYPE_STRING: case eb_lib_compiler.TYPE_TEXT: case eb_lib_compiler.TYPE_UUID: return 'string'; case eb_lib_compiler.TYPE_BIGINT: case eb_lib_compiler.TYPE_INT: case eb_lib_compiler.TYPE_INT16: case eb_lib_compiler.TYPE_INT32: case eb_lib_compiler.TYPE_INT64: case eb_lib_compiler.TYPE_INTEGER: case eb_lib_compiler.TYPE_UINT16: case eb_lib_compiler.TYPE_UINT32: case eb_lib_compiler.TYPE_UINT64: return 'integer'; case eb_lib_compiler.TYPE_BIN: case eb_lib_compiler.TYPE_BINARY: case eb_lib_compiler.TYPE_BLOB: return 'resource'; case eb_lib_compiler.TYPE_BOOL: case eb_lib_compiler.TYPE_BOOLEAN: return 'boolean'; case eb_lib_compiler.TYPE_DATE: case eb_lib_compiler.TYPE_DATETIME: case eb_lib_compiler.TYPE_DATETIME_TZ: case eb_lib_compiler.TYPE_TIME: return '\\DateTime'; case eb_lib_compiler.TYPE_FLOAT: return 'float'; } return 'mixed'; } //# sourceMappingURL=doctrine.js.map