如何在 Strapi 中复制/克隆记录?

Nic*_*ick 3 strapi

我想从数据库中另一条记录的克隆开始,而不必再次填写所有这些字段。目前这是非常乏味的,必须打开两个选项卡(一个带有现有记录,另一个带有新记录)并复制粘贴数据。

如何在 Strapi 中克隆/复制记录?

Gre*_*own 8

您需要按照编辑扩展指南编辑此文件

????admin
?   ????src
?       ????containers
?       ?   ????EditView
?       ?   ?       Header.js
?       ?   ????EditViewDataManagerProvider
?       ?       ?   index.js
?       ?       ????utils
?       ?               cleanData.js
?       ?               index.js
?       ????translations
?               en.json
????config
?       routes.json
????controllers
?       ContentManager.js
????services
        ContentManager.js
Run Code Online (Sandbox Code Playgroud)

头文件.js

????admin
?   ????src
?       ????containers
?       ?   ????EditView
?       ?   ?       Header.js
?       ?   ????EditViewDataManagerProvider
?       ?       ?   index.js
?       ?       ????utils
?       ?               cleanData.js
?       ?               index.js
?       ????translations
?               en.json
????config
?       routes.json
????controllers
?       ContentManager.js
????services
        ContentManager.js
Run Code Online (Sandbox Code Playgroud)

并通过handleClone这里

if (!isCreatingEntry) {
      headerActions.unshift(
        {
          label: formatMessage({
            id: `${pluginId}.containers.Edit.clone`,
          }),
          color: 'primary',
          onClick: (e) => {
            handleClone(e);
          },
          type: 'button',
          style: {
            paddingLeft: 15,
            paddingRight: 15,
            fontWeight: 600,
          },
        },
        {
          label: formatMessage({
            id: 'app.utils.delete',
          }),
          color: 'delete',
          onClick: () => {
            toggleWarningDelete();
          },
          type: 'button',
          style: {
            paddingLeft: 15,
            paddingRight: 15,
            fontWeight: 600,
          },
        },
      );
    }
Run Code Online (Sandbox Code Playgroud)

EditViewDataManagerProvider/index.js

const {
    deleteSuccess,
    initialData,
    layout,
    redirectToPreviousPage,
    resetData,
    handleClone,
    setIsSubmitting,
    slug,
    clearData,
  } = useDataManager();
Run Code Online (Sandbox Code Playgroud)

并提供handleClone给 EditViewDataManagerContext.Provider.value

EditViewDataManagerProvider/utils/cleanData.js

const handleClone = async (event) => {
    event.preventDefault();

    // Create yup schema
    const schema = createYupSchema(
      currentContentTypeLayout,
      {
        components: get(allLayoutData, 'components', {}),
      },
      true
    );

    try {
      // Validate the form using yup
      await schema.validate(modifiedData, { abortEarly: false });
      // Set the loading state in the plugin header
      const filesToUpload = getFilesToUpload(modifiedData);
      // Remove keys that are not needed
      // Clean relations
      const cleanedData = cleanData(
        cloneDeep(modifiedData),
        currentContentTypeLayout,
        allLayoutData.components,
        true
      );

      const formData = new FormData();

      formData.append('data', JSON.stringify(cleanedData));

      Object.keys(filesToUpload).forEach((key) => {
        const files = filesToUpload[key];

        files.forEach((file) => {
          formData.append(`files.${key}`, file);
        });
      });

      // Change the request helper default headers so we can pass a FormData
      const headers = {};
      const method = 'POST';
      const endPoint = `${slug}/clone/${modifiedData.id}`;

      emitEvent(isCreatingEntry ? 'willCloneEntry' : 'willCloneEntry');

      try {
        // Time to actually send the data
        await request(
          getRequestUrl(endPoint),
          {
            method,
            headers,
            body: formData,
            signal,
          },
          false,
          false
        );
        emitEvent(isCreatingEntry ? 'didCloneEntry' : 'didCloneEntry');
        dispatch({
          type: 'CLONE_SUCCESS',
        });
        strapi.notification.success(`${pluginId}.success.record.clone`);
        // strapi.notification.success('Entry cloned!');

        redirectToPreviousPage();
      } catch (err) {
        console.error({ err });
        const error = get(
          err,
          ['response', 'payload', 'message', '0', 'messages', '0', 'id'],
          'SERVER ERROR'
        );

        setIsSubmitting(false);
        emitEvent(isCreatingEntry ? 'didNotCloneEntry' : 'didNotCloneEntry', {
          error: err,
        });
        strapi.notification.error(error);
      }
    } catch (err) {
      const errors = getYupInnerErrors(err);
      console.error({ err, errors });

      dispatch({
        type: 'CLONE_ERRORS',
        errors,
      });
    }
  };
Run Code Online (Sandbox Code Playgroud)

只需复制 EditViewDataManagerProvider/utils/index.js

添加在 config/routes.json


{
  "method": "POST",
  "path": "/explorer/:model/clone/:id",
  "handler": "ContentManager.clone",
  "config": {
    "policies": ["routing"]
  }
}

Run Code Online (Sandbox Code Playgroud)

在controllers/ContentManager.js 中添加类似于 create clone 的方法

const cleanData = (retrievedData, currentSchema, componentsSchema, clone = false) => {
  const getType = (schema, attrName) =>
    get(schema, ['attributes', attrName, 'type'], '');
  const getSchema = (schema, attrName) =>
    get(schema, ['attributes', attrName], '');
  const getOtherInfos = (schema, arr) =>
    get(schema, ['attributes', ...arr], '');

  const recursiveCleanData = (data, schema) => Object.keys(data).reduce((acc, current) => {
      const attrType = getType(schema.schema, current);
      const valueSchema = getSchema(schema.schema, current);
      const value = get(data, current);
      const component = getOtherInfos(schema.schema, [current, 'component']);
      const isRepeatable = getOtherInfos(schema.schema, [
        current,
        'repeatable',
      ]);
      let cleanedData;


      switch (attrType) {
        case 'string': {
          if (clone && valueSchema.unique) {
            cleanedData = `${value}_clone`;
          } else {
            cleanedData = value;
          }
          break;
        }
        case 'json':
          try {
            cleanedData = JSON.parse(value);
          } catch (err) {
            cleanedData = value;
          }

          break;
        case 'date':
        case 'datetime':
          cleanedData =
            value && value._isAMomentObject === true
              ? value.toISOString()
              : value;
          break;
        case 'media':
          if (getOtherInfos(schema.schema, [current, 'multiple']) === true) {
            cleanedData = value
              ? helperCleanData(
                  value.filter(file => !(file instanceof File)),
                  'id'
                )
              : null;
          } else {
            cleanedData =
              get(value, 0) instanceof File ? null : get(value, 'id', null);
          }
          break;
        case 'component':
          if (isRepeatable) {
            cleanedData = value
              ? value.map((data) => {
                  const subCleanedData = recursiveCleanData(
                    data,
                    componentsSchema[component]
                  );

                  return subCleanedData;
                })
              : value;
          } else {
            cleanedData = value
              ? recursiveCleanData(value, componentsSchema[component])
              : value;
          }
          break;
        case 'dynamiczone':
          cleanedData = value.map((componentData) => {
            const subCleanedData = recursiveCleanData(
              componentData,
              componentsSchema[componentData.__component]
            );

            return subCleanedData;
          });
          break;
        default:
          cleanedData = helperCleanData(value, 'id');
      }


      acc[current] = cleanedData;

      if (clone && (current === '_id' || current === 'id')) {
        acc[current] = undefined;
      }

      return acc;
    }, {});

  return recursiveCleanData(retrievedData, currentSchema);
};
Run Code Online (Sandbox Code Playgroud)

最后是 services/ContentManager.js


{
  "method": "POST",
  "path": "/explorer/:model/clone/:id",
  "handler": "ContentManager.clone",
  "config": {
    "policies": ["routing"]
  }
}

Run Code Online (Sandbox Code Playgroud)

不要忘记更新 translations/:lang.json 中的翻译

"containers.Edit.clone": "clone button",
"success.record.clone": "notification"
Run Code Online (Sandbox Code Playgroud)

不要忘记重建您的管理 UI

npm run build
Run Code Online (Sandbox Code Playgroud)

  • 也许您可以为其创建拉取请求或新插件。 (8认同)