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
JavaScript
"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 += `
/**
* 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 += `
`;
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