• ¡Bienvenido a XenFácil!

    Estás viendo el sitio como Invitado. Para poder participar en este sitio

    y obtendrás privilegios adicionales, acceso a otras áreas y mucho mas.

    ¡Es gratis!


    ¿Ya eres miembro? Inicia sesión

Noticia ¿Qué hay de nuevo? para desarrolladores en XenForo 2

lms

Administrador
Mensajes
7.741
Puntuación de reacciones
2.163
Puntos
2.613
Sitio web
xenfacil.com
País
España
Versión de XenForo
2.1.x
PayPal
Donar dinero a este usuario
Página web
Web
El ¿Qué hay de nuevo? en XenForo 2.0 está enfocado a los cambios visibles para usuarios finales y administradores. Mientras se está incluyendo algunas referencias de los cambios orientados a los desarrolladores, creemos que ya es tiempo de hacer un tema ¿qué hay de nuevo? para los desarrolladores.

Si no se ha leído, vale la pena leer algunas partes de las actualizaciones de desarrollo anteriores cuando se habla de cambios relacionados con el desarrollo.
Así, sin un orden en particular...

Entidades y buscadores
Las entidades son la versión de los data writers de XF2. Sin embargo, es mucho más extensible:
Se usan para leer y escribir. La mayoría del trabajo que se hace con los datos se hará a través de una entidad.

Automáticamente manejan los valores de codificado y descodificado. Por ejemplo, si se define una columna como JSON, solo das la estructura de datos (probablemente una matríz) y se codificará al guardar. Similarmente, cuando se obtiene un registro, se decodificará el valor obtenido en la matríz al acceder a él.

Pueden tener getters (adquiridores) para envolver la lógica alrededor de coger un valor sin que necesariamente esté almacenado en la BD. Por ejemplo, los títulos de las opciones son frases en la actualidad por lo que no hay columnas de título en la tabla option. Sin embargo, se puede llamar $option->title o $option['title'] y devolverá la frase objeto.

Definen sus relaciones y pueden cargarse dinámicamente según necesidades. $post->Thread->Forum devolverá el foro donde radica el mensaje, independientemente de si ha incluido el tema y / o los registros del foro al cargar el mensaje.
Soportan una reutilización horizontal a través del sistema de "comportamiento" (behavior). Actualmente sólo se agregan comportamientos extra cuando se ejecuta una acción, Pero los ejemplos son para ajustar automáticamente los 'Me Gusta' cuando se elimina el contenido o se oculta y se aplica el cambio de estado a una entidad.

Pueden agregarse otros métodos para exponer cualquier otra funcionalidad que se precise. Llámese a $user->authenticate($password) para ver si la contraseña es correcta o a $thread->canView() para determinar si el visitante actual puede ver el tema.

Los Finders (encontradores) son esencialmente constructores de consultas. Un caso genérico, provee métodos como where u order que permite aplicar condiciones, clasificaciones y 'Me Gusta' en la estructura de la BD de una entidad. Sin embargo, también puede revelar algunos métodos específicos de la entidad para proporcionar funcionalidad más compleja. Éste es un ejemplo:
PHP:
$finder = \XF::finder('XF:Thread');
$finder->where('node_id', $nodeId)
    ->with(['Forum', 'FirstPost'])
    ->unreadOnly()
    ->order('last_post_date', 'desc')
    ->limitByPage($page, $perPage);
$threads = $finder->fetch();
Muchos de ellos son métodos genéricos del finder aunque unreadOnly() es un método específico del tema que aplica lógica basada en el visitante actual y complejas condiciones para limitar los resultados a sólo los no leídos. Aunque XF1 tiene algunos conceptos similares (implementados muy diferentemente), las restricciones complejas no son generalmente reutilizables y causan una gran cantidad de código repetido. En XF2, estas restricciones son generalmente compartidas por el objeto Finder y puede reutilizarse. Como ejemplo, es trivial mostrar solo temas que el usuario ha publicado aunque también lo son los temas no leídos.

Hay mucho más en este tema. Es un cambio mayor en la capa de acceso a datos de XenForo y probablemente uno de los más importantes conceptos de aquirir en XF2. Como hay otras cosas que cubrir, añadamos unos puntos:
  1. Cuando se obtienen varias entidades, se devuelve un objeto colector en vez de una matríz pelada. Puede obtenerse la matríz si se quiere, aunque el objeto colector tiene varios métodos auxiliares (pluck, filter, groupBy, etc).
  2. Las relaciones entre entidades pueden devolver muchas entidades. Para hacer un ejemplo específico, un tema puede estar seguido por mucha gente, aunque generalmente solo se pide para una persona en un momento. $thread->Watch[$userId] devolverá el registro seguido para el $userId especificado. No se espera cargar todos los seguidores. El valor "Watch" es una colección "escasa" de entidades que cargan registros individuales según demanda (o se puede llamarlo para obtenerlos todos).
  3. Se puede anhelar cargar un solo registro de una relación "a muchos": $finder->with("Watch|$userId").
  4. Se pueden construir los Finders en cualquier orden. Y puede aplicarse una condición tras establecer un orden o límite.
  5. No es necesario crear manualmente un finder para cada caso. Existen métodos comunes en objetos "repositorio" que establecen los finders para casos de base común. Éstos repositorios también pueden mantener lógica/acciones relativas a un tipo de entidad. Algunos ejemplos incluyen métodos como countUnreadThreadsInForum(\) o rebuildThreadUserPostCounters(). Aunque no se trata de un grupo vertedero genérico, también se pueden usar para varios métodos auxiliares, particularmente cuando manipulan la BD directamente o no se relacionan con una sola entidad.
  6. La mayoría de los métodos relativos al finder devuelven el finder en lugar de resultados. Esto hace que puedan ser manipulados en casos específicos o extendidos para para agregar comportamientos extra. Por ejemplo, findThreadsForForumView() puede extender para incluir una relación específica (aunque esto podría hacerse en el finder thread).
Vale, la he liado, Han sido algo más que unos pocos.

Sistema orientado a servicios
La mayoría de las acciones comlejas se han movido ahora a objetos de servicio que representan sólo esa acción. La mayoría de los servicios funcionan con el enfoque de tipo "configurar y funcionar"; Se configuran al gusto y ellos llaman al método para completar la acción.
Aunque este ejemplo se ha publicad en la actualización de desarrollo, sigue siendo un buen ejemplo:
PHP:
/** @var \XF\Service\Thread\Mover $mover */
$mover = $this->app()->service('XF:Thread\Mover', $thread);
if ($options['alert'])
{
    $mover->setSendAlert(true, $options['alert_reason']);
}
if ($options['notify_watchers'])
{
    $mover->setNotifyWatchers();
}
if ($options['redirect'])
{
    $mover->setRedirect(true, $options['redirect_length']);
}
if ($options['prefix_id'] !== null)
{
    $mover->setPrefix($options['prefix_id']);
}
$mover->move($targetForum);
Este es más o menos el tipo de código que se ve en el controlador. $options pueden basarse en entradas de usuario. El código actual para realizar los cambios está en el servico 'mover tema'. Actualmente esto incluye mover el tema y cambiar el prefijo de tema, crear una redirección, notificar a los seguidores, enviar las alertas y registrar la acción. Cualquier ubicación desde la que se quiera mover un tema deberá llamar a este servicio con la oportuna configuración y hacerlo; no existe duplicidad de "lógica de negocios".

Ahora este ejemplo es algo simple en pro de mantenerse enfocado en el servicio. Sin embargo, lo que realmente se encontrará es que la mayoría de las llamadas de servicio de XF2 crean y configuran el servicio para un método y luego pasan el servicio a otro método para llevar a cabo la acción. Actualmente esto hay en el método mover del controlador de tema:
PHP:
$this->setupThreadMove($thread, $targetForum)->move($targetForum);
Ya que los servicios configuran todo en el método setupThreadMove, esto crea un perfecto punto de enganche (hook)/inyección de comportamiento personalizado. Agrega tus opciones personalizadas al servico configurándolo al extender el método setupThreadMove. No hay que crear variables globales piratas / claves de registro para intentar inyectar los cambios extra.

Acciones de formulario
Hablando de evitar variables piratas globales y claves de registro para hacer extensiones...
No todas las acciones usan servicios. Varias de las páginas del panel de control admin encajan en el modelo CRUD: Crear, Read -leer-, Update -actualizar-, Delete -eliminar-. Toman la entrada del usuario, la pasan a la entidad y la guardan. No intentan manipular la entrada del usuario por ninguna vía ni acción.

En vez de crear un servicio para cada caso, se usa un objeto FormAction que permite imitar el comportamiento "configurar y funcionar" de los servicios. Estas acciones se reducen en varias fases: configurar, validar, aplicar, completar. Pueden agregarse comnportamientos a cada uno y cuando esté listo, se "ejecutará" la acción. Si ocurre cualquier error durante la validación, se rechazan y nunca se aplicarán.

El caso más simple debe ser el cómo se guarda un estilo:
PHP:
$form = $this->formAction();
$input = $this->filter([
    'parent_id' => 'uint',
    'title' => 'str',
    'description' => 'str',
    'user_selectable' => 'bool'
]);
$form->basicEntitySave($style, $input);
basicEntitySave() es un método auxiliar que pone la entrada en la entidad durante la configuración, la valida mediante preSave() y la guarda en el paso en que se aplica.

Como en los servicios, esto pasa en un método y se devuelve la acción del formulario desde él y se ejecuta:
PHP:
$this->styleSaveProcess($style)->run();
Por lo que si se ha modificado el formulario para tener entradas extra, sólo hay que inyectarlas usando algo así como:
PHP:
$form->setup(function() use ($style, $customValue)
{
    $style->custom_value = $customValue;
});

Uso de LESS (CSS Preprocesador) y CSS estilado BEM
Ya se discutió esto en el pasado por lo que no vamos a entrar en muchos más detalles aunque conviene mencionarlo otra vez.

Por lo general, usamos LESS en vez de CSS puro. Si no se está familiarizado con LESS, recomendamos comprobar la documentación de su sitio web para hacerse una mejor idea sobre su funcionamiento. Se verá que es muy similar a CSS por lo que no existe todavía una curva de aprendizaje. Proporciona funcionalidades tales como varias manipulaciones de funciones (como en oscurecer colores) y soporta selectores anidados. Significativamente, también proporciona "mezcladores" que permiten crear funciones con facilidad para encapsular patrones comunes de CSS (sí, hemos considerado SCSS varias veces. No se ajusta a nuestras necesidades. No lo vamos a cambiar por LESS.)

Hablando en general, nuestro CSS se estructura en líneas de BEM: bloque, elemento, modificador. Esto hace que nuestras clases estén organizadas por líneas de .block, .block-row, .block-row--minor. Si se explora el HTML que se ve en XF2, debe ser mucho más clarificador.

Sistema unificado de plantillas, plantillas en el sistema de archivos
No sólo existe un sistema de plantillas para satisfacer al público, también están las plantillas de admin y de email. Se han separado por tipo, aunque son idénticas en sí mismo. Esto conlleva:
Misma sintaxis de plantillas disponible para todo (aunque no desearás usar las funciones más complejas de las plantillas de email).

Puede incluirse una plantilla de diferente tipo a la actual sólo con prefijar el tipo ( public:thread_view).

Mismo sistema de edición para cada tipo de plantilla por lo que se comparte la funcionalidad (incluye histórico, combinación y modificación de plantillas, por ejemplo).

Esto hace que las plantillas de email sean personalizables ahora. El email se envolverá usando el estilo predeterminado del sitio. Los componentes del email se dividen ahora en partes específicas de la plantilla base reproducida con etiquetas como <mail:subject>. También se incluye ahora automáticamente en los emails CSS en línea para mejorar la visualización en diferentes clientes.

Finalmente, todas las plantillas se sirven desde el sistema de archivos. Esto hace que se beneficien de poner en caché el código de operación.

Sintaxis extendida de plantillas
Mientras la sintaxis de plantillas es bastante similar a XF1, la funcionalidad se expande en XF2. Véamos un ejemplo del perfil de usuario:
HTML:
<xf:if is="$user.canPostOnProfile()">
    <xf:set var="$firstProfilePost" value="{$profilePosts|first}" />
    <xf:macro template="profile_post_macros" name="submit"
        arg-user="{$user}"
        arg-lastDate="{{ $firstProfilePost.post_date ?: 0 }}"
    />
</xf:if>
<xf:if is="$profilePosts is not empty">
    <xf:foreach loop="$profilePosts" value="$profilePost">
        <xf:macro template="profile_post_macros"
            name="{{ $profilePost.message_state == 'deleted' ? 'profile_post_deleted' : 'profile_post' }}"
            arg-profilePost="{$profilePost}"
        />
    </xf:foreach>
<xf:else />
    <div class="block-row">{{ phrase('there_no_messages_on_xs_profile_yet', {'name': $user.username}) }}</div>
</xf:if>

{$profilePosts|first} usa un filtro para obtener sólo el primer valor de una collección/matríz. Los filtros tienen más o menos otra sintaxis sobre la manipulación del primer parámetro. Pueden encadenarse y llevar argumentos: {$value|replace('from', 'to')|to_upper}

Las condiciones pueden ser is e is not, tan conocidas como probadas. Actualmente sólo se revela empty aunque también puede expandirse. Estas pruebas pueden envolver una lógica compleja de un modo fácil de comprender.

Se lanzan expresiones complejas con {{ ... }}. Pueden verse llamadas a funciones de plantilla aquí (phrase), uso de operadores ternarios y creación de pares de nombre-valor como en JavaScript con llaves. Están disponibles otros operadores (de coincidencia y concatenación) y valores 'no cadenas' como true, false y null que también pueden representarse.
Aquí, la única cosa grande son las llamadas a macros.

Las macros son similares a las inclusiones de plantilla pero con argumentos particulares. Es mucho más sencillo visualizar qué opciones están disponibles en una macro determinada y las variables de macro no contaminan el ámbito de la plantilla que se llama. La macro del mensaje de perfil se define como:
HTML:
<xf:macro name="profile_post"
    arg-profilePost="!"
    arg-showTargetUser="{{ false }}"
    arg-allowInlineMod="{{ true }}"
>
Define todos los argumentos que están disponibles dentro del cuerpo de la macro como {$profilePost}, {$showTargetUser}, {$allowInlineMod}. El valor de los argumentos de la macro es su valor predeterminado cuando no se provee; ! indica que es un argumento obligatorio y dará error si no se provee.

De nuevo, esto es mucho más de lo que se pretende cubrir en esta visión de conjunto.

Nuevo sistema de rutas
En XF1, debe escribirse código personalizado en PHP para analizar la coincidencia de la ruta y generar la mayoría de las URLs. En XF2, usamos sintaxis personalizada para hacer ambas cosas automáticamente en la mayoría de los casos.

Los temas sirven con ejemplo simple. El prefijo de ruta "threads" se introduce y se obtiene lo siguiente como un "formato de ruta": :int<thread_id,title>/:page
(ya que el prefijo de ruta coincide efectivamente con threads/:int<thread_id,title>/:page.)

La parte :int<thread_id,title> indica que es un parámetro basado en un entero. Para construir un enlace de salida, obtenemos el entero de la clave thread_id de los datos que se le pasan. Si se pasa el título en los datos, será "deshechado" y se agregará el entero ID tal y como se ve en la URL de ésta página. Para las URL entrantes, ésto obtiene el entero a través de una expresión regular cuando coincide con el formato.

:page es un atajo para generar la parte page-123 de un vínculo. En este caso, se ve como parámetro en el vínculo de la página. Si se encuentra, se coloca en la URL y se elimina de los parámetros. Para analizar lo entrante, si coincide (puede estar vacío), agregará el número de la página a los parámetros pasados a un controlador.

Cuando se genera un vínculo en una plantilla, se hace a través de una llamada como esta: {{ link('members/following', $user, {'page': 3}) }} Uso éste particular ejemplo por la parte "following". Normalmente, ésta será la acción; el vínculo se cionstruirá con la coincidencia básica "members". Sin embargo, si existe una ruta que coincida con el prefijo "members" y con el sub-nombre "following", éste se usará en su lugar. Ésto es verdad aquí ya que se construye un vínculo como el siguiente:
Insertar CODE, HTML o PHP:
members/:int<user_id,username>/following/:page
Para la coincidencia de rutas entrantes, se comprobará ésta ruta antes que la ruta básica members; si coincide se usará.
Este sistema sub-nombre permite cambiar comportamientos (como mover donde el parámetro de la página iría normalmente en éste ejemplo) o sub-agrupando (tal y como se usa en cosas como el gestor de recursos y en los complementos).

Al igual que con los otros sistemas discutidos hoy, existe disponible un lote de opciones más para el sistema de enrutamiento.

Gestión del tipo de contenido
Los manejadores del tipo de contenido se gestionan ahora a través del panel de control y se incluirán como parte de los complementos. No más gestión manual.

Para explicar esto, los tipos de contenido en sí mismos no se adjuntan a los complementos. Sólo a los manejadores específicos. Esto evita ciertos errores y problemas que ocurrieron en XF1.
La mayorís de manejadores del tipo de contenido son bastante similares a lo que se encuentra en XF1. Por ejemplo, attachment_handler_class representaría a XF\Attachment\Post en los mensajes. Existen algunos nuevos, sin embargo, incluyendo:
  1. entity, que representa un tipo de contenido de una entidad (y por lo tanto a un finder y repositorio)
  2. phrase, que representa un tipo de contenido a un nombre de frase (versión en singular)
  3. phrase_plural, que representa un tipo de contenido a un nombre de frase (versión en plural)
...y otras sistemas de clases manejadoras que no existen en XF1

Esto se aleja de todo. Hay mucho más para poder cubrirlo en un día, por lo que ésto se continuará más adelante.

Tema original (en inglés): What's new for developers in XenForo 2 (part 1) - XenForo 2 Demo

Salud2
 
Última edición:
Arriba