Ir al contenido

Migración de Plugins de WordPress

Muchos complementos de WordPress pueden migrarse a EmDash. El modelo de complemento es diferente—TypeScript en lugar de PHP, hooks en lugar de acciones/filtros, almacenamiento estructurado en lugar de wp_options—pero la mayoría de las funcionalidades se mapean de forma clara.

No todos los complementos tienen sentido migrar. Evalúa los candidatos antes de comenzar.

Buenos candidatos

Campos personalizados, complementos de SEO, procesadores de contenido, extensiones de la interfaz de administración, analíticas, compartir en redes sociales, formularios

Malos candidatos

Funciones multisitio, integraciones de WooCommerce/Gutenberg, complementos que parchean el núcleo interno de WordPress

wp-content/plugins/my-plugin/
├── my-plugin.php # Main file with plugin header
├── includes/
│ ├── class-admin.php
│ └── class-api.php
└── admin/
└── js/

WordPress usa add_action() y add_filter() con nombres de hooks de texto. EmDash usa hooks tipados declarados en la definición del complemento.

WordPressEmDashNotas
register_activation_hook()plugin:installSe ejecuta una vez en la primera instalación
Plugin habilitadoplugin:activateSe ejecuta cuando se habilita
Plugin deshabilitadoplugin:deactivateSe ejecuta cuando se deshabilita
register_uninstall_hook()plugin:uninstallevent.deleteData indica la elección del usuario
WordPressEmDashNotas
wp_insert_post_datacontent:beforeSaveDevuelve contenido modificado o lanza una excepción para cancelar
save_postcontent:afterSaveEfectos secundarios después de guardar
before_delete_postcontent:beforeDeleteDevuelve false para cancelar
deleted_postcontent:afterDeleteLimpieza después de la eliminación
php
add_action('save_post', function($post_id, $post, $update) {
if ($post->post_type !== 'product') return;
$price = get_post_meta($post_id, 'price', true);
if ($price > 1000) {
update_post_meta($post_id, 'is_premium', true);
}
}, 10, 3);
WordPressEmDashNotas
wp_handle_upload_prefiltermedia:beforeUploadValidar o transformar
add_attachmentmedia:afterUploadReaccionar después de la carga
php
$api_key = get_option('my_plugin_api_key', '');
update_option('my_plugin_api_key', 'abc123');
delete_option('my_plugin_api_key');

Tablas Personalizadas → Colecciones de Almacenamiento

Sección titulada «Tablas Personalizadas → Colecciones de Almacenamiento»
php
global $wpdb;
$table = $wpdb->prefix . 'my_plugin_items';
// Insertar
$wpdb->insert($table, ['name' => 'Item 1', 'status' => 'active']);
// Consultar
$items = $wpdb->get_results(
"SELECT \* FROM $table WHERE status = 'active' LIMIT 10"
);

WordPress usa la API de Configuración para formularios de administración. EmDash usa un esquema declarativo que genera automáticamente la interfaz de usuario.

php
add_action('admin_init', function() {
register_setting('my_plugin', 'my_plugin_api_key');
add_settings_section('main', 'Settings', null, 'my-plugin');
add_settings_field('api_key', 'API Key', function() {
$value = get_option('my_plugin_api_key');
echo '<input type="text" name="my_plugin_api_key"
value="' . esc_attr($value) . '">';
}, 'my-plugin', 'main');
});

Las páginas de administración de WordPress son PHP. EmDash usa componentes React.

tsx title="src/admin.tsx"
import { useState, useEffect } from "react";
export const widgets = {
summary: function SummaryWidget() {
const [count, setCount] = useState(0);
useEffect(() => {
fetch("/_emdash/api/plugins/my-plugin/status")
.then((r) => r.json())
.then((data) => setCount(data.count));
}, []);
return <div>Total de elementos: {count}</div>
;
},
};
export const pages = {
settings: function SettingsPage() {
// React component for settings page
return <div>Settings content</div>
;
},
};

Registrar en la definición del complemento:

typescript title="src/index.ts"
admin: {
entry: "@my-org/my-plugin/admin",
pages: [{ path: "/settings", label: "Dashboard" }],
widgets: [{ id: "summary", title: "Summary", size: "half" }],
},
php
register_rest_route('my-plugin/v1', '/items', [
'methods' => 'GET',
'callback' => function($request) {
global $wpdb;
$items = $wpdb->get_results("SELECT * FROM items LIMIT 50");
return new WP_REST_Response($items);
},
]);

Las rutas están disponibles en /_emdash/api/plugins/{plugin-id}/{route-name}.

  1. Analiza el complemento de WordPress

    Documenta lo que hace: hooks, operaciones de base de datos, páginas de administración, endpoints REST.

  2. Mapea a conceptos de EmDash

    Hooks de WordPress → Hooks de EmDash. wp_options → ctx.kv. Tablas personalizadas → Colecciones de almacenamiento. Páginas de administración → Componentes React. Endpoints REST → Rutas del complemento.

  3. Crea el esqueleto del complemento

    src/index.ts
    import { definePlugin } from "emdash";
    export function createPlugin() {
    return definePlugin({
    id: "my-ported-plugin",
    version: "1.0.0",
    capabilities: [],
    storage: {},
    hooks: {},
    routes: {},
    admin: {},
    });
    }
  4. Implementa en orden

    Almacenamiento → Hooks → Interfaz de administración → Rutas

  5. Prueba exhaustivamente

    Verifica que los hooks se disparen correctamente, que el almacenamiento funcione y que la interfaz de administración se renderice.

php
add_filter('wp_insert_post_data', function($data, $postarr) {
if ($data['post_type'] !== 'post') return $data;
$content = strip_tags($data['post_content']);
$word_count = str_word_count($content);
$read_time = ceil($word_count / 200);
if (!empty($postarr['ID'])) {
update_post_meta($postarr['ID'], '_read_time', $read_time);
}
return $data;
}, 10, 2);

Los plugins deben declarar las capacidades requeridas para el sandbox de seguridad:

CapacidadProporcionaCaso de Uso
network:fetchctx.http.fetch()Llamadas a API externas
read:contentctx.content.get(), list()Lectura de contenido del CMS
write:contentctx.content.create(), etc.Modificación de contenido
read:mediactx.media.get(), list()Lectura de medios
write:mediactx.media.getUploadUrl()Subida de medios

Sin estado global — Usa almacenamiento en lugar de variables globales.

Todo asíncrono — Siempre usa await en llamadas de almacenamiento y API.

Sin SQL directo — Usa colecciones de almacenamiento estructurado.

Sin sistema de archivos — Usa la API de medios para archivos.