/** * WPML compatibility functions * * @global array $duplicated_posts Array to store the posts being duplicated. * * @package Yoast\WP\Duplicate_Post * @since 3.2 */ add_action( 'admin_init', 'duplicate_post_wpml_init' ); /** * Add handlers for WPML compatibility. */ function duplicate_post_wpml_init() { if ( defined( 'ICL_SITEPRESS_VERSION' ) ) { add_action( 'dp_duplicate_page', 'duplicate_post_wpml_copy_translations', 10, 3 ); add_action( 'dp_duplicate_post', 'duplicate_post_wpml_copy_translations', 10, 3 ); add_action( 'shutdown', 'duplicate_wpml_string_packages', 11 ); } } global $duplicated_posts; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: Renaming a global variable is a BC break. $duplicated_posts = []; /** * Copy post translations. * * @global SitePress $sitepress Instance of the Main WPML class. * @global array $duplicated_posts Array of duplicated posts. * * @param int $post_id ID of the copy. * @param WP_Post $post Original post object. * @param string $status Status of the new post. */ function duplicate_post_wpml_copy_translations( $post_id, $post, $status = '' ) { global $sitepress; global $duplicated_posts; remove_action( 'dp_duplicate_page', 'duplicate_post_wpml_copy_translations', 10 ); remove_action( 'dp_duplicate_post', 'duplicate_post_wpml_copy_translations', 10 ); $current_language = $sitepress->get_current_language(); $trid = $sitepress->get_element_trid( $post->ID ); if ( ! empty( $trid ) ) { $translations = $sitepress->get_element_translations( $trid ); $new_trid = $sitepress->get_element_trid( $post_id ); foreach ( $translations as $code => $details ) { if ( $code !== $current_language ) { if ( $details->element_id ) { $translation = get_post( $details->element_id ); if ( ! $translation ) { continue; } $new_post_id = duplicate_post_create_duplicate( $translation, $status ); if ( ! is_wp_error( $new_post_id ) ) { $sitepress->set_element_language_details( $new_post_id, 'post_' . $translation->post_type, $new_trid, $code, $current_language ); } } } } // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: see above. $duplicated_posts[ $post->ID ] = $post_id; } } /** * Duplicate string packages. * * @global array() $duplicated_posts Array of duplicated posts. */ function duplicate_wpml_string_packages() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: renaming the function would be a BC-break. global $duplicated_posts; foreach ( $duplicated_posts as $original_post_id => $duplicate_post_id ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. $original_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $original_post_id ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. $new_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $duplicate_post_id ); if ( is_array( $original_string_packages ) ) { foreach ( $original_string_packages as $original_string_package ) { $translated_original_strings = $original_string_package->get_translated_strings( [] ); foreach ( $new_string_packages as $new_string_package ) { $cache = new WPML_WP_Cache( 'WPML_Package' ); $cache->flush_group_cache(); $new_strings = $new_string_package->get_package_strings(); foreach ( $new_strings as $new_string ) { if ( isset( $translated_original_strings[ $new_string->name ] ) ) { foreach ( $translated_original_strings[ $new_string->name ] as $language => $translated_string ) { do_action( // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. 'wpml_add_string_translation', $new_string->id, $language, $translated_string['value'], $translated_string['status'] ); } } } } } } } } 標準 Odoo メタデータ抽出/ 連携テーブルの抽出クエリ – Raqqa

標準 Odoo メタデータ抽出/ 連携テーブルの抽出クエリ

フィールド

-- 抽出モデルとベース言語を指定。全モデルは target_model を NULL に
WITH params AS (
  SELECT
    'res.partner'::text AS target_model,
    'en_US'::text       AS base_lang
),

-- public スキーマ内の各テーブルの PK 列を取得
pk_by_table AS (
  SELECT
    c.relname                                              AS model_table,  -- 物理テーブル名
    array_agg(a.attname ORDER BY a.attnum)                 AS pk_columns    -- 主キー列配列
  FROM pg_index i
  JOIN pg_class c      ON c.oid = i.indrelid
  JOIN pg_namespace n  ON n.oid = c.relnamespace AND n.nspname = 'public'
  JOIN pg_attribute a  ON a.attrelid = c.oid AND a.attnum = ANY(i.indkey)
  WHERE i.indisprimary
  GROUP BY c.relname
)

SELECT
  m.model                                        AS model,
  replace(m.model, '.', '_')                     AS model_table,
  f.name                                         AS field_name,
  f.ttype                                        AS ttype,

  /* ★ label_i18n:既に JSON オブジェクトならそのまま、その他は base_lang で包む */
  COALESCE(
    CASE
      WHEN pg_typeof(f.field_description)::text IN ('json','jsonb')
           AND jsonb_typeof(f.field_description::jsonb) = 'object'
        THEN jsonb_strip_nulls(f.field_description::jsonb)
    END,
    CASE
      WHEN pg_typeof(f.field_description)::text IN ('json','jsonb')
           AND jsonb_typeof(f.field_description::jsonb) = 'string'
        THEN jsonb_build_object(p.base_lang, btrim(f.field_description::text, '"'))
      ELSE CASE
             WHEN f.field_description IS NOT NULL AND f.field_description::text <> ''
             THEN jsonb_build_object(p.base_lang, f.field_description::text)
             ELSE '{}'::jsonb
           END
    END
  )                                               AS label_i18n,

  'generated'::text                               AS code_status,

  /* ★ notes(helpをTEXTで採用):json文字列→中身、jsonオブジェクト→base_langの値、他はそのまま */
  COALESCE(
    CASE
      WHEN pg_typeof(f.help)::text IN ('json','jsonb')
           AND jsonb_typeof(f.help::jsonb) = 'string' THEN btrim(f.help::text, '"')
      WHEN pg_typeof(f.help)::text IN ('json','jsonb')
           AND jsonb_typeof(f.help::jsonb) = 'object' THEN f.help::jsonb ->> p.base_lang
      ELSE f.help::text
    END,
    NULL
  )                                               AS notes,

  CASE f.state WHEN 'manual' THEN 'studio' ELSE 'code' END AS origin,

  -- ★ デフォルト非表示(ポータルINSERT時にそのまま使える)
  'invisible'::text                               AS show_invisible,

  -- ★ ここから PK 情報
  COALESCE(pk.pk_columns, ARRAY[]::text[])        AS "テーブルPK列配列",
  CASE WHEN pk.pk_columns IS NOT NULL
         AND f.name = ANY(pk.pk_columns) THEN true ELSE false
  END                                             AS "このフィールドはPKか",

  -- 参考:日本語表示用(そのまま INSERT も可能)
  m.model                                        AS "モデル技術名",
  replace(m.model, '.', '_')                     AS "モデル物理名",
  f.name                                         AS "フィールド技術名",
  CASE f.ttype
    WHEN 'char'      THEN '文字列'
    WHEN 'text'      THEN 'テキスト'
    WHEN 'integer'   THEN '整数'
    WHEN 'float'     THEN '小数'
    WHEN 'boolean'   THEN '真偽'
    WHEN 'date'      THEN '日付'
    WHEN 'datetime'  THEN '日時'
    WHEN 'json'      THEN 'JSON'
    WHEN 'many2one'  THEN '参照'
    WHEN 'one2many'  THEN '複数参照'
    WHEN 'many2many' THEN '多対多'
    ELSE '文字列'
  END                                            AS "データ型"
FROM ir_model m
JOIN ir_model_fields f ON f.model_id = m.id
LEFT JOIN pk_by_table   pk ON pk.model_table = replace(m.model, '.', '_')
CROSS JOIN params p
WHERE (p.target_model IS NULL OR m.model = p.target_model)
ORDER BY m.model, f.name;

show_invisibleで表示されるフィールドを制御

連携テーブルの抽出クエリ

-- ★ 抽出対象モデル。全モデルは NULL に
WITH params AS (
  SELECT
    'res.partner'::text AS target_model,
    'ja_JP'::text       AS target_lang,  -- ← 取りたい言語
    'en_US'::text       AS base_lang     -- ← フォールバック基準
),

-- 対象モデル(技術名→物理名)
models AS (
  SELECT m.id AS model_id, m.model AS local_model, replace(m.model,'.','_') AS local_table
  FROM ir_model m
  CROSS JOIN params p
  WHERE p.target_model IS NULL OR m.model = p.target_model
),

-- 全モデル(参照先解決用)
all_models AS (
  SELECT m.id AS model_id, m.model AS model_tech, replace(m.model,'.','_') AS model_table
  FROM ir_model m
),

-- public スキーマ内テーブルの PK 列
pk_by_table AS (
  SELECT
    c.relname                              AS table_name,
    array_agg(a.attname ORDER BY a.attnum) AS pk_columns
  FROM pg_index i
  JOIN pg_class c     ON c.oid = i.indrelid
  JOIN pg_namespace n ON n.oid = c.relnamespace AND n.nspname = 'public'
  JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum = ANY(i.indkey)
  WHERE i.indisprimary
  GROUP BY c.relname
),

/* =========================================================
   フィールド名 → ラベル辞書(model_id, field_name 単位)
   - field_description が json/jsonb の object: そのまま言語キーで参照
   - json/jsonb の string: テキスト化(btrim('"'))
   - それ以外: text 化
   target_lang → base_lang → 'en_US' → 技術名 の順でフォールバック
========================================================= */
field_labels AS (
  SELECT
    f.model_id,
    f.name AS field_name,
    CASE
      WHEN pg_typeof(f.field_description)::text IN ('json','jsonb')
           AND jsonb_typeof(f.field_description::jsonb) = 'string'
        THEN btrim(f.field_description::text, '"')
      ELSE f.field_description::text
    END AS label_txt,
    CASE
      WHEN pg_typeof(f.field_description)::text IN ('json','jsonb')
           AND jsonb_typeof(f.field_description::jsonb) = 'object'
        THEN f.field_description::jsonb
      ELSE NULL
    END AS label_obj
  FROM ir_model_fields f
),

/* ===== many2one =====  local.m2o_col = remote.id */
rels_m2o AS (
  SELECT
    'many2one'                              AS rel_kind,
    f.ttype                                  AS local_ttype,        -- ★ 型 追加
    mo.local_model                          AS local_model,
    mo.local_table                          AS local_table,
    f.name                                  AS local_field,
    -- 日本語ラベル(local_field)
    COALESCE(
      (SELECT fl.label_obj ->> p.target_lang FROM field_labels fl WHERE fl.model_id = mo.model_id AND fl.field_name = f.name),
      (SELECT fl.label_obj ->> p.base_lang   FROM field_labels fl WHERE fl.model_id = mo.model_id AND fl.field_name = f.name),
      (SELECT fl.label_obj ->> 'en_US'       FROM field_labels fl WHERE fl.model_id = mo.model_id AND fl.field_name = f.name),
      (SELECT fl.label_txt                   FROM field_labels fl WHERE fl.model_id = mo.model_id AND fl.field_name = f.name),
      f.name
    )                                        AS local_field_label_ja,

    cm.model_tech                           AS remote_model,
    cm.model_table                          AS remote_table,
    COALESCE(pk_local.pk_columns,  ARRAY['id']) AS local_pk_columns,
    COALESCE(pk_remote.pk_columns, ARRAY['id']) AS remote_pk_columns,
    ARRAY[f.name]                           AS join_local_cols,
    ARRAY['id']                             AS join_remote_cols,
    (mo.local_model = cm.model_tech)        AS is_self_reference,

    -- ★ リモート側の表示用フィールドと日本語ラベル
    remdisp.field_name                      AS remote_display_field,
    COALESCE(
      (SELECT fl.label_obj ->> p.target_lang FROM field_labels fl WHERE fl.model_id = cm.model_id AND fl.field_name = remdisp.field_name),
      (SELECT fl.label_obj ->> p.base_lang   FROM field_labels fl WHERE fl.model_id = cm.model_id AND fl.field_name = remdisp.field_name),
      (SELECT fl.label_obj ->> 'en_US'       FROM field_labels fl WHERE fl.model_id = cm.model_id AND fl.field_name = remdisp.field_name),
      (SELECT fl.label_txt                   FROM field_labels fl WHERE fl.model_id = cm.model_id AND fl.field_name = remdisp.field_name),
      CASE WHEN remdisp.field_name = 'id' THEN 'ID' ELSE remdisp.field_name END
    )                                        AS remote_display_label_ja,

    NULL::text                              AS inverse_field,
    NULL::text                              AS inverse_field_label_ja,
    NULL::text                              AS junction_table,
    NULL::text                              AS junction_col_local,
    NULL::text                              AS junction_col_remote
  FROM models mo
  JOIN ir_model_fields f ON f.model_id = mo.model_id AND f.ttype='many2one'
  LEFT JOIN all_models cm        ON cm.model_tech = f.relation
  LEFT JOIN pk_by_table pk_local  ON pk_local.table_name  = mo.local_table
  LEFT JOIN pk_by_table pk_remote ON pk_remote.table_name = cm.model_table
  -- ★ 相手モデルの表示用フィールドを決定(name → display_name → PK)
  LEFT JOIN LATERAL (
    SELECT
      CASE
        WHEN EXISTS (SELECT 1 FROM ir_model_fields rf WHERE rf.model_id = cm.model_id AND rf.name='name') THEN 'name'
        WHEN EXISTS (SELECT 1 FROM ir_model_fields rf WHERE rf.model_id = cm.model_id AND rf.name='display_name') THEN 'display_name'
        ELSE COALESCE(pk_remote.pk_columns[1], 'id')
      END AS field_name
  ) remdisp ON TRUE
  CROSS JOIN params p
),

/* ===== one2many =====  local.id = remote.inverse_m2o */
rels_o2m AS (
  SELECT
    'one2many'                              AS rel_kind,
    f.ttype                                  AS local_ttype,        -- ★ 型 追加
    mo.local_model                          AS local_model,
    mo.local_table                          AS local_table,
    f.name                                  AS local_field,
    -- 日本語ラベル(local_field)
    COALESCE(
      (SELECT fl.label_obj ->> p.target_lang FROM field_labels fl WHERE fl.model_id = mo.model_id AND fl.field_name = f.name),
      (SELECT fl.label_obj ->> p.base_lang   FROM field_labels fl WHERE fl.model_id = mo.model_id AND fl.field_name = f.name),
      (SELECT fl.label_obj ->> 'en_US'       FROM field_labels fl WHERE fl.model_id = mo.model_id AND fl.field_name = f.name),
      (SELECT fl.label_txt                   FROM field_labels fl WHERE fl.model_id = mo.model_id AND fl.field_name = f.name),
      f.name
    )                                        AS local_field_label_ja,

    cm.model_tech                           AS remote_model,
    cm.model_table                          AS remote_table,
    COALESCE(pk_local.pk_columns,  ARRAY['id']) AS local_pk_columns,
    COALESCE(pk_remote.pk_columns, ARRAY['id']) AS remote_pk_columns,
    COALESCE(pk_local.pk_columns,  ARRAY['id']) AS join_local_cols,
    ARRAY[COALESCE(inv.inv_name, 'id')]     AS join_remote_cols,
    (mo.local_model = cm.model_tech)        AS is_self_reference,

    -- ★ 相手モデルの表示用フィールドと日本語ラベル
    remdisp.field_name                      AS remote_display_field,
    COALESCE(
      (SELECT fl.label_obj ->> p.target_lang FROM field_labels fl WHERE fl.model_id = cm.model_id AND fl.field_name = remdisp.field_name),
      (SELECT fl.label_obj ->> p.base_lang   FROM field_labels fl WHERE fl.model_id = cm.model_id AND fl.field_name = remdisp.field_name),
      (SELECT fl.label_obj ->> 'en_US'       FROM field_labels fl WHERE fl.model_id = cm.model_id AND fl.field_name = remdisp.field_name),
      (SELECT fl.label_txt                   FROM field_labels fl WHERE fl.model_id = cm.model_id AND fl.field_name = remdisp.field_name),
      CASE WHEN remdisp.field_name = 'id' THEN 'ID' ELSE remdisp.field_name END
    )                                        AS remote_display_label_ja,

    inv.inv_name                            AS inverse_field,
    -- 日本語ラベル(inverse_field)
    COALESCE(
      (SELECT fl.label_obj ->> p.target_lang FROM field_labels fl WHERE fl.model_id = inv.inv_model_id AND fl.field_name = inv.inv_name),
      (SELECT fl.label_obj ->> p.base_lang   FROM field_labels fl WHERE fl.model_id = inv.inv_model_id AND fl.field_name = inv.inv_name),
      (SELECT fl.label_obj ->> 'en_US'       FROM field_labels fl WHERE fl.model_id = inv.inv_model_id AND fl.field_name = inv.inv_name),
      (SELECT fl.label_txt                   FROM field_labels fl WHERE fl.model_id = inv.inv_model_id AND fl.field_name = inv.inv_name),
      inv.inv_name
    )                                        AS inverse_field_label_ja,

    NULL::text                              AS junction_table,
    NULL::text                              AS junction_col_local,
    NULL::text                              AS junction_col_remote
  FROM models mo
  JOIN ir_model_fields f ON f.model_id = mo.model_id AND f.ttype='one2many'
  LEFT JOIN all_models cm        ON cm.model_tech = f.relation
  LEFT JOIN pk_by_table pk_local  ON pk_local.table_name  = mo.local_table
  LEFT JOIN pk_by_table pk_remote ON pk_remote.table_name = cm.model_table
  -- 逆参照列(子の many2one)+その model_id
  LEFT JOIN LATERAL (
    SELECT cf.name AS inv_name, cf.model_id AS inv_model_id
    FROM ir_model_fields cf
    WHERE cf.model_id = cm.model_id
      AND cf.ttype = 'many2one'
      AND cf.relation = mo.local_model
    ORDER BY
      CASE WHEN f.relation_field IS NOT NULL AND cf.name = f.relation_field THEN 0 ELSE 1 END,
      CASE WHEN cf.name = replace(mo.local_model,'.','_') || '_id' THEN 0 ELSE 1 END,
      cf.id
    LIMIT 1
  ) AS inv ON TRUE
  -- 相手モデルの表示用フィールド
  LEFT JOIN LATERAL (
    SELECT
      CASE
        WHEN EXISTS (SELECT 1 FROM ir_model_fields rf WHERE rf.model_id = cm.model_id AND rf.name='name') THEN 'name'
        WHEN EXISTS (SELECT 1 FROM ir_model_fields rf WHERE rf.model_id = cm.model_id AND rf.name='display_name') THEN 'display_name'
        ELSE COALESCE(pk_remote.pk_columns[1], 'id')
      END AS field_name
  ) remdisp ON TRUE
  CROSS JOIN params p
),

/* ===== many2many =====  junction.col_local = local.id, junction.col_remote = remote.id */
rels_m2m AS (
  SELECT
    'many2many'                              AS rel_kind,
    f.ttype                                  AS local_ttype,        -- ★ 型 追加
    mo.local_model                           AS local_model,
    mo.local_table                           AS local_table,
    f.name                                   AS local_field,
    -- 日本語ラベル(local_field)
    COALESCE(
      (SELECT fl.label_obj ->> p.target_lang FROM field_labels fl WHERE fl.model_id = mo.model_id AND fl.field_name = f.name),
      (SELECT fl.label_obj ->> p.base_lang   FROM field_labels fl WHERE fl.model_id = mo.model_id AND fl.field_name = f.name),
      (SELECT fl.label_obj ->> 'en_US'       FROM field_labels fl WHERE fl.model_id = mo.model_id AND fl.field_name = f.name),
      (SELECT fl.label_txt                   FROM field_labels fl WHERE fl.model_id = mo.model_id AND fl.field_name = f.name),
      f.name
    )                                        AS local_field_label_ja,

    cm.model_tech                            AS remote_model,
    cm.model_table                           AS remote_table,
    COALESCE(pk_local.pk_columns,  ARRAY['id']) AS local_pk_columns,
    COALESCE(pk_remote.pk_columns, ARRAY['id']) AS remote_pk_columns,
    COALESCE(pk_local.pk_columns,  ARRAY['id']) AS join_local_cols,
    COALESCE(pk_remote.pk_columns, ARRAY['id']) AS join_remote_cols,
    (mo.local_model = cm.model_tech)         AS is_self_reference,

    -- ★ 相手モデルの表示用フィールドと日本語ラベル
    remdisp.field_name                      AS remote_display_field,
    COALESCE(
      (SELECT fl.label_obj ->> p.target_lang FROM field_labels fl WHERE fl.model_id = cm.model_id AND fl.field_name = remdisp.field_name),
      (SELECT fl.label_obj ->> p.base_lang   FROM field_labels fl WHERE fl.model_id = cm.model_id AND fl.field_name = remdisp.field_name),
      (SELECT fl.label_obj ->> 'en_US'       FROM field_labels fl WHERE fl.model_id = cm.model_id AND fl.field_name = remdisp.field_name),
      (SELECT fl.label_txt                   FROM field_labels fl WHERE fl.model_id = cm.model_id AND fl.field_name = remdisp.field_name),
      CASE WHEN remdisp.field_name = 'id' THEN 'ID' ELSE remdisp.field_name END
    )                                        AS remote_display_label_ja,

    NULL::text                               AS inverse_field,
    NULL::text                               AS inverse_field_label_ja,
    NULLIF(f.relation_table,'')              AS junction_table,
    NULLIF(f.column1,'')                     AS junction_col_local,
    NULLIF(f.column2,'')                     AS junction_col_remote
  FROM models mo
  JOIN ir_model_fields f ON f.model_id = mo.model_id AND f.ttype='many2many'
  LEFT JOIN all_models cm        ON cm.model_tech = f.relation
  LEFT JOIN pk_by_table pk_local  ON pk_local.table_name  = mo.local_table
  LEFT JOIN pk_by_table pk_remote ON pk_remote.table_name = cm.model_table
  -- 相手モデルの表示用フィールド
  LEFT JOIN LATERAL (
    SELECT
      CASE
        WHEN EXISTS (SELECT 1 FROM ir_model_fields rf WHERE rf.model_id = cm.model_id AND rf.name='name') THEN 'name'
        WHEN EXISTS (SELECT 1 FROM ir_model_fields rf WHERE rf.model_id = cm.model_id AND rf.name='display_name') THEN 'display_name'
        ELSE COALESCE(pk_remote.pk_columns[1], 'id')
      END AS field_name
  ) remdisp ON TRUE
  CROSS JOIN params p
),

all_rels AS (
  SELECT * FROM rels_m2o
  UNION ALL
  SELECT * FROM rels_o2m
  UNION ALL
  SELECT * FROM rels_m2m
)

SELECT
  -- 分類(自己参照は m2o/o2m のみ)
  CASE
    WHEN rel_kind IN ('many2one','one2many') AND is_self_reference THEN '自己参照(親子関係)'
    WHEN rel_kind = 'many2one'  THEN '多対一(参照)'
    WHEN rel_kind = 'one2many'  THEN '一対多(子テーブル)'
    WHEN rel_kind = 'many2many' THEN '多対多(中間テーブル)'
  END                                         AS 分類,

  rel_kind                                    AS rel_kind_raw,
  local_ttype,                                -- ★ 追加:型
  local_model, local_table,
  local_field,       local_field_label_ja,
  remote_model, remote_table,

  -- ★ 追加:リモートの表示用フィールドと日本語ラベル
  remote_display_field,
  remote_display_label_ja,

  inverse_field,     inverse_field_label_ja,
  join_local_cols, join_remote_cols,
  junction_table, junction_col_local, junction_col_remote,
  is_self_reference
FROM all_rels
ORDER BY 分類, local_model, rel_kind_raw, local_field;

このテーブル情報を基に連携用途をOpenAIに推測させる


Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です