Construyendo un paquete de transporte sin instalar MODX



Escribir sus paquetes para MODX no es f√°cil para un principiante, y un desarrollador experimentado a veces se lo pasa en grande. Pero el principiante tiene miedo, y el experimentado entiende :).

Esta publicación habla sobre cómo puede escribir y construir un paquete de componentes para MODX sin instalar y configurar MODX. El nivel está por encima de la media, por lo que es posible que tengas que estrujarte el cerebro en algunos casos, pero vale la pena.

Pido detalles bajo cat.

Una vez, cuando apareci√≥ MODX Revolution, estaba en la versi√≥n beta temprana, los desarrolladores a√ļn no sab√≠an c√≥mo trabajar con √©l y c√≥mo escribir complementos para √©l. Bueno, excepto por el equipo que estudi√≥ el CMS. Y el equipo, debo decir, tuvo √©xito en parte y proporcion√≥ al sistema en s√≠ mismo para recopilar convenientemente paquetes que luego se pueden instalar a trav√©s del repositorio, lo que parece l√≥gico. Pero desde entonces, han pasado muchos a√Īos y los requisitos para los paquetes y su montaje han cambiado un poco.

Copiar y pegar es malo, aunque no siempre


En los √ļltimos meses, me preguntaba por qu√©, para crear un paquete para MODX, debe instalarlo, crear una base de datos, crear un administrador, etc. Tantas acciones extra. No, no hay nada de malo en eso si lo configura una vez y luego lo usa. Muchos lo hacen Pero, ¬Ņqu√© pasa cuando quieres confiar la asamblea al gui√≥n e ir a tomar un caf√© t√ļ mismo?

Sucedió que los creadores de MODX estaban acostumbrados a trabajar con MODX y agregaron clases al paquete directamente al kernel. También escribieron los primeros componentes, los primeros scripts de compilación, que luego fueron utilizados como ejemplos por otros desarrolladores que simplemente copiaron la solución, no siempre profundizando en la esencia de lo que estaba sucediendo. Y lo hice

Pero la tarea es automatizar el ensamblaje del paquete, preferiblemente en el servidor, siempre con un conjunto mínimo de software requerido, con recursos mínimos y, por lo tanto, con mayor velocidad. La tarea se estableció y después de estudiar la fuente, Jason frenó en el chat y encontró una solución.

Y cual?


Lo primero que descubrí es que el código responsable de compilar el paquete se encuentra directamente en la biblioteca xPDO, y en MODX solo hay clases de contenedor que proporcionan una API más conveniente y son algo más fáciles de trabajar, pero solo si MODX está instalado. Por lo tanto, probablemente solo se pueda usar xPDO de alguna manera, pero en el código el constructor del objeto xPDO requiere especificar datos para la conexión de la base de datos.

public function __construct( $dsn, $username = '', $password = '', $options = [], $driverOptions= null ); 

Después de interrogar a Jason, quedó claro que, aunque los parámetros deben establecerse, la conexión física real a la base de datos se produce exactamente en el momento en que es necesario. Carga perezosa en todo su esplendor. El segundo problema ha sido resuelto.

El tercer problema fue el problema de conectar xPDO al proyecto. Composer me vino a la mente de inmediato, pero la versión 2.x en la que se ejecuta el MODX actual no es compatible con Composer, y la rama 3.x usa espacios de nombres y los nombres de clase se escriben de manera diferente que en 2.x, lo que genera conflictos y errores. En general, incompatible. Luego tuve que usar herramientas git y conectar xPDO como un submódulo.

Cómo usar submódulos



Primero, lea la documentación sobre ellos.

Luego, si este es un proyecto nuevo, debe agregar un submódulo:

 $ git submodule add https://github.com/username/reponame 

Este comando clonar√° e instalar√° un subm√≥dulo en su proyecto. Luego, deber√° agregar la carpeta de subm√≥dulos a su repositorio con el comando git add. No agregar√° toda la carpeta con el subm√≥dulo, pero agregar√° a git solo un enlace al √ļltimo commit desde el subm√≥dulo.

Para que otro desarrollador pueda clonar el proyecto con todas las dependencias, debe crear una configuración .gitmodules para submódulos. En el proyecto Slackify, es así:

 [submodule "_build/xpdo"] path = _build/xpdo url = https://github.com/modxcms/xpdo.git branch = 2.x 

Después de eso, al clonar, solo especifique el indicador recursivo y git descargará todos los repositorios dependientes.
Como resultado, tenemos xPDO, xPDO se puede usar sin conectarse a la base de datos, si no es necesario, xPDO se puede conectar al código del componente como una dependencia externa (submódulo git). Ahora la implementación del script de compilación.

Entendamos


Describir√© el script de compilaci√≥n del complemento Slackify publicado recientemente por m√≠. Este componente es gratuito y est√° disponible p√ļblicamente en GitHub, lo que facilitar√° el autoestudio.

Conectar xPDO


Omitimos la tarea de constantes con el nombre del paquete y otras llamadas necesarias y conectamos xPDO.

 require_once 'xpdo/xpdo/xpdo.class.php'; require_once 'xpdo/xpdo/transport/xpdotransport.class.php'; $xpdo = xPDO::getInstance('db', [ xPDO::OPT_CACHE_PATH => __DIR__ . '/../cache/', xPDO::OPT_HYDRATE_FIELDS => true, xPDO::OPT_HYDRATE_RELATED_OBJECTS => true, xPDO::OPT_HYDRATE_ADHOC_FIELDS => true, xPDO::OPT_CONNECTIONS => [ [ 'dsn' => 'mysql:host=localhost;dbname=xpdotest;charset=utf8', 'username' => 'test', 'password' => 'test', 'options' => [xPDO::OPT_CONN_MUTABLE => true], 'driverOptions' => [], ] ] ]); 

Agregué el submódulo xPDO a la carpeta _build, que solo necesitamos en la etapa de desarrollo y ensamblaje del paquete y que no entrará en el archivo principal del componente. La segunda copia de xPDO en el sitio con Live MODX no la necesitamos.

En la configuraci√≥n de conexi√≥n xPDO, configuro el nombre de la base de datos en dsn , pero no juega ning√ļn papel. Es importante que la carpeta de cach√© dentro de xPDO sea grabable. Eso es todo, xPDO se inicializa.

Haciendo un truco complicado con clases


Al usar el MODX instalado al crear el paquete, todo es simple, tomamos y creamos un objeto de la clase que necesitamos. MODX realmente encuentra la clase requerida, encuentra la implementación necesaria para esta clase (la clase con el postfix _mysql), que depende de la base de datos y luego crea el objeto deseado (debido a esta característica, puede obtener errores al construir el paquete que la clase * _mysql no encontrado, esto no da miedo). Sin embargo, no tenemos una base ni una implementación. Necesitamos reemplazar de alguna manera la clase deseada, que estamos haciendo.

 class modNamespace extends xPDOObject {} class modSystemSetting extends xPDOObject {} 

Creamos una clase ficticia (stub), que es necesaria para crear el objeto deseado. Esto no debería hacerse si xPDO no verifica específicamente a qué clase pertenece el objeto. Pero él lo comprueba.

Pero hay casos especiales en los que necesita hacer un poco más que solo definir una clase. Estos son casos de dependencias entre clases. Por ejemplo, necesitamos agregar un complemento a la categoría. En el código, solo $category->addOne($plugin); pero en nuestro caso esto no funcionará.

Si alguna vez examinó el esquema de base de datos MODX , probablemente vio elementos como agregado y compuesto. Está escrito sobre ellos en la documentación , pero si de una manera simple, describen la relación entre clases.

En nuestro caso, puede haber varios complementos en una categoría, de los cuales el elemento agregado es responsable de la clase modCategory . Por lo tanto, dado que tenemos una clase sin una implementación concreta, debemos indicar esta conexión a mano. Es más fácil hacer esto anulando el método getFKDefinition :

 class modCategory extends xPDOObject { public function getFKDefinition($alias) { $aggregates = [ 'Plugins' => [ 'class' => 'modPlugin', 'local' => 'id', 'foreign' => 'category', 'cardinality' => 'many', 'owner' => 'local', ] ]; return isset($aggregates[$alias]) ? $aggregates[$alias] : []; } } 

En nuestro componente, solo se usan complementos, por lo que agregamos enlaces solo para ellos. Después de eso, el método addMany de la clase modCategory puede agregar fácilmente los complementos necesarios a la categoría y luego al paquete.

Crea un paquete


 $package = new xPDOTransport($xpdo, $signature, $directory); 

Como puede ver, todo es muy, muy simple. Aquí necesitábamos pasar el parámetro $xpdo , que inicializamos al principio. Si no fuera por este momento, no habría problema 2. $signature - el nombre del paquete, incluida la versión, $directory - el lugar donde el paquete se colocará cuidadosamente. De dónde provienen estas variables, compruébelo usted mismo en la fuente.

Cree un espacio de nombres y agréguelo al paquete


Necesitamos un espacio de nombres para vincular l√©xicos y configuraciones del sistema. En nuestro caso, solo por esto, otros a√ļn no se consideran.

 $namespace = new modNamespace($xpdo); $namespace->fromArray([ 'id' => PKG_NAME_LOWER, 'name' => PKG_NAME_LOWER, 'path' => '{core_path}components/' . PKG_NAME_LOWER . '/', ]); $package->put($namespace, [ xPDOTransport::UNIQUE_KEY => 'name', xPDOTransport::PRESERVE_KEYS => true, xPDOTransport::UPDATE_OBJECT => true, xPDOTransport::RESOLVE_FILES => true, xPDOTransport::RESOLVE_PHP => true, xPDOTransport::NATIVE_KEY => PKG_NAME_LOWER, 'namespace' => PKG_NAME_LOWER, 'package' => 'modx', 'resolve' => null, 'validate' => null ]); 

La primera parte es clara para cualquiera que haya escrito c√≥digo para MODX. El segundo, con la adici√≥n al paquete, es un poco m√°s complicado. El m√©todo put toma 2 par√°metros: el objeto en s√≠ y una matriz de par√°metros que describen este objeto y su posible comportamiento al momento de instalar el paquete. Por ejemplo, xPDOTransport::UNIQUE_KEY => 'name' significa que el campo de name con el nombre del propio espacio de nombres como valor se usar√° para el espacio de nombres como una clave √ļnica en la base de datos. Puede leer m√°s sobre los par√°metros en la documentaci√≥n de xPDO , y mejor estudiando el c√≥digo fuente.

Del mismo modo, puede agregar otros objetos, como la configuración del sistema.

 $package->put($setting, [ xPDOTransport::UNIQUE_KEY => 'key', xPDOTransport::PRESERVE_KEYS => true, xPDOTransport::UPDATE_OBJECT => true, 'class' => 'modSystemSetting', 'resolve' => null, 'validate' => null, 'package' => 'modx', ]); 

Crea una categoría


Con la adición de una categoría, tuve la mayor mordaza cuando lo descubrí todo. Los elementos puestos en una categoría en el modelo xPDO deben pertenecer a esta categoría, es decir. estar anidado en él, y solo entonces la categoría en sí debe estar anidada en el paquete. Y al mismo tiempo, debe tener en cuenta las relaciones entre las clases, que ya he descrito anteriormente. Tomó bastante tiempo entenderlo, darse cuenta y aplicarlo correctamente.

 $package->put($category, [ xPDOTransport::UNIQUE_KEY => 'category', xPDOTransport::PRESERVE_KEYS => false, xPDOTransport::UPDATE_OBJECT => true, xPDOTransport::ABORT_INSTALL_ON_VEHICLE_FAIL => true, xPDOTransport::RELATED_OBJECTS => true, xPDOTransport::RELATED_OBJECT_ATTRIBUTES => [ 'Plugins' => [ xPDOTransport::UNIQUE_KEY => 'name', xPDOTransport::PRESERVE_KEYS => false, xPDOTransport::UPDATE_OBJECT => false, xPDOTransport::RELATED_OBJECTS => true ], 'PluginEvents' => [ xPDOTransport::UNIQUE_KEY => ['pluginid', 'event'], xPDOTransport::PRESERVE_KEYS => true, xPDOTransport::UPDATE_OBJECT => false, xPDOTransport::RELATED_OBJECTS => true ] ], xPDOTransport::NATIVE_KEY => true, 'package' => 'modx', 'validate' => $validators, 'resolve' => $resolvers ]); 

Parece monstruoso, pero no tan visto. Un parámetro importante es xPDOTransport::RELATED_OBJECTS => true , que indica que la categoría tiene elementos anidados que también deben empaquetarse y luego instalarse.

Dado que la mayoría de los módulos contienen varios elementos (fragmentos, fragmentos, complementos), la categoría con elementos es la pieza más importante del paquete de transporte. Por lo tanto, es aquí donde se especifican los validadores y los solucionadores, que se realizan durante la instalación del paquete.
Los validadores se realizan antes de la instalación, los solucionadores, después.

Casi se me olvida, antes de empacar la categoría, necesitamos agregarle nuestros elementos. Así:

 $plugins = include $sources['data'] . 'transport.plugins.php'; if (is_array($plugins)) { $category->addMany($plugins, 'Plugins'); } 

Agregue otros datos al paquete.


En el paquete, debe agregar otro archivo con una licencia, un archivo con un registro de cambios y un archivo con una descripción del componente. Si es necesario, puede agregar otro script especial a través del atributo setup-options , que mostrará la ventana antes de instalar el paquete. Esto es cuando en lugar de "Instalar" el botón "Opciones de instalación". Y a partir de la versión de MODX 2.4 se hizo posible especificar dependencias entre paquetes usando el atributo requires , y en él también se puede especificar la versión de PHP y MODX.

 $package->setAttribute('changelog', file_get_contents($sources['docs'] . 'changelog.txt')); $package->setAttribute('license', file_get_contents($sources['docs'] . 'license.txt')); $package->setAttribute('readme', file_get_contents($sources['docs'] . 'readme.txt')); $package->setAttribute('requires', ['php' => '>=5.4']); $package->setAttribute('setup-options', ['source' => $sources['build'] . 'setup.options.php']); 

Empacamos


 if ($package->pack()) { $xpdo->log(xPDO::LOG_LEVEL_INFO, "Package built"); } 

Eso es todo, recoja el paquete terminado en _packages , bien, o desde donde configuró el ensamblaje.

Cual es el resultado?


El resultado superó mis expectativas, ya que este enfoque, aunque impone algunas limitaciones y en algunos lugares agrega algunos inconvenientes, pero gana en términos de posibilidades de aplicación.

Para construir el paquete, solo ejecute 2 comandos:

 git clone --recursive git@github.com:Alroniks/modx-slackify.git cd modx-slackify/_build && php build.transport.php 

El primero es la clonación del repositorio y sus submódulos. Un parámetro importante es --recursive , gracias a él git descargará e instalará, además del código del componente en sí, todas las dependencias descritas como submódulos.

El segundo es construir el paquete directamente. Después de eso, puede recoger el package-1.0.0-pl.transport.zip terminado _packages carpeta _packages y cargarlo, por ejemplo, en el repositorio.

Las perspectivas son amplias. Por ejemplo, puede configurar un enlace en GitHub que, despu√©s de comprometerse con una rama, ejecutar√° un script en su servidor que recopilar√° el paquete y lo colocar√° en todos los sitios que tenga. O suba la nueva versi√≥n a alg√ļn repositorio, y en ese momento preparar√° caf√© para usted, como dije al principio. O puede crear y escribir pruebas para el m√≥dulo y ejecutar la ejecuci√≥n de prueba y construir a trav√©s de Jenkins o Travis. S√≠, un mont√≥n de escenarios que se te pueden ocurrir. Con este enfoque, hacer esto ahora es mucho m√°s f√°cil.

Haga preguntas, intente responder.

PD: No pases, pon una estrella de Slackify en GitHub , por favor.

Source: https://habr.com/ru/post/439420/


All Articles