corcel / acf

Advanced Custom Fields (ACF) plugin for Corcel
127 stars 100 forks source link

Recomendation #49

Closed drosendo closed 6 years ago

drosendo commented 7 years ago

After trying to use this plugin I really had issues with perfomance, querying like 15000 times due to my ACF being built in a massive block with option (imagine a Visual Composer but built with ACF). Due to this the result is very slow.

So my approach will be much cleaner, and want some opinion on it. What i'm going to do is on wordpress add an action (https://codex.wordpress.org/Plugin_API/Action_Reference/save_post), that when the user hits save:

  1. Check if page has ACF
  2. If have ACF run getfields (https://www.advancedcustomfields.com/resources/get_fields/)
  3. Save the buetifull structured array into a a post meta (https://codex.wordpress.org/Function_Reference/update_post_meta), lets called it, "acf_corcel"
  4. In Corcel just get that post meta $post = Post::find(31); echo $post->meta->link;

I think this will be much faster and easy, opinions?

Cheers, David

drosendo commented 7 years ago

So here goes my code to whoever wants it:

On wordpress:

add_action('save_post', 'acf_corcel', 10, 3);
    /**
     * Save post metadata when a post is saved.
     *
     * @param int $post_id The post ID.
     * @param post $post The post object.
     * @param bool $update Whether this is an existing post being updated or not.
     */
    public function acf_corcel($post_id, $post, $update) {

        $fields = get_fields($post_id);

        if ($fields) {
            update_post_meta($post_id, 'acf_corcel', $fields);
        }
    }

Then on Laravel:

$post = Page::slug('home')->first();
$fields = unserialize($post->meta->acf_corcel);

Result: Queries: 2 Queries Memory: 2MB Time: 154ms DD:

array:1 [▼
  "data" => array:1 [▼
    "layout" => array:7 [▼
      0 => array:5 [▼
        "option" => "container-fluid"
        "background" => "None"
        "image" => false
        "color" => ""
        "container" => array:1 [▶]
      ]
      1 => array:5 [▶]
      2 => array:5 [▶]
      3 => array:5 [▶]
      4 => array:5 [▶]
      5 => array:5 [▶]
      6 => array:5 [▶]
    ]
  ]
]

Previous code:

$post = Page::slug('home')->first();
$fields = $post->acf->layout;

Result: Queries: 13206 Queries Memory: 134MB Time: 12.8s DD:

array:1 [▼
  "data" => Collection {#7589 ▼
    #items: array:7 [▼
      0 => array:15 [▼
        "option" => "container-fluid"
        "background" => "None"
        "container_0_rows_0_row_0_col_0__extra_small" => "12"
        "container_0_rows_0_row_0_col_0__small" => "12"
        "container_0_rows_0_row_0_col_0__medium" => "12"
        "container_0_rows_0_row_0_col_0__large" => "12"
        "container_0_rows_0_row_0_col_0__extra_large" => "12"
        "container_0_rows_0_row_0_col" => Collection {#626 ▶}
        "container_0_rows_0_row_0_slide_0_type" => "image"
        "container_0_rows_0_row_0_slide_0_image" => Image {#632 ▶}
        "container_0_rows_0_row_0_slide_0_link" => "none"
        "container_0_rows_0_row_0_slide" => Collection {#687 ▶}
        "container_0_rows_0_row" => Collection {#1450 ▶}
        "container_0_rows" => Collection {#1717 ▶}
        "container" => Collection {#2263 ▼
          #items: array:1 [▶]
        }
      ]
      1 => array:23 [▶]
      2 => array:96 [▶]
      3 => array:58 [▶]
      4 => array:25 [▶]
      5 => array:14 [▶]
      6 => array:12 [▶]
    ]
  }
]

From my result just deconstruct to get your "special field" data.

Cheers, David

benrolfe commented 7 years ago

Looks a good solution, have you run into any limitations?

drosendo commented 7 years ago

None, I now do whatever I want with the information. Data is updated in WordPress after each save, so I always get the current data.

Cheers, David

jgrossi commented 7 years ago

@drosendo can you make this a PR, please?

drosendo commented 7 years ago

@jgrossi Maybe I don't know what you mean with "PR" (Pull request?), but there is nothing here to be added to your code... except the WordPress part, but this is a case to case... :)

DanDvoracek commented 6 years ago

@drosendo this is great! Great thinking, Thanks! This was exactly what I needed 👍

drosendo commented 6 years ago

I think there is nothing much to add here... feel free to suggest better ways of getting the structured data.

drosendo commented 6 years ago

@jgrossi

Update for WPML support:

On Wordpress:

add_action('save_post', 'acf_corcel', 10, 3);
    /**
     * Save post metadata when a post is saved.
     *
     * @param int $post_id The post ID.
     * @param post $post The post object.
     * @param bool $update Whether this is an existing post being updated or not.
     */
    public function acf_corcel($post_id, $post, $update) {
    global $sitepress;
        $lang = '';
        if (class_exists('SitePress')) {
            $lang = ICL_LANGUAGE_CODE;
            if ($lang == 'pt-pt') // If the same as default language clear it so that I use acf_corcel
                $lang = '';
        }

        $fields = get_fields($post_id);

        if ($fields) {
            update_post_meta($post_id, 'acf_corcel'. $lang, $fields); // if lang is set then the meta will be "acf_corcel**lang**"
        }
    }

Then on laravel:

//Im using mcamara/laravel-localization
        $lang = '';
        if (\LaravelLocalization::getCurrentLocale() !== 'pt')
            $lang = \LaravelLocalization::getCurrentLocale();

$post = Page::slug('home')->get(); // Will get all posts IF translation shares same slug

        if (count($post) > 1) {
            foreach ($post as $k => $v) {

                $meta_field = 'acf_corcel' . $lang;
                $data = $v->meta->$meta_field; //gets the translated information
                if (unserialize($data)) // If found
                    $fields = unserialize($data); //Voilá
            }
        } else {
            $fields= unserialize($post[0]->meta->acf_corcel . $lang);
        }

Any recommendation on this better? At least it's working...

Cheers, David

jgrossi commented 6 years ago

anyone to create a PR with that solution, please? 🙏🎉

cjke commented 6 years ago

@drosendo @jgrossi - I was hitting a similar performance issue (and have raised a seperate PR discussion to try to resolve it in the core library).

For now, I have taken a similar approach as @drosendo - this one deep dives, recursively. get_fields() seems to resolved nested content. For example - if you have a repeater of Posts, it won't get the meta on the Posts.

function recurse($value) 
{
        if(is_array($value)) {
            foreach ($value as $key => $item) {
                $value[$key] = recurse($item);
            }
        } else if($value instanceof WP_Post) {
            $value = array_merge($value->to_array(), get_fields($value->ID));
        }
        return $value;
}

add_action('save_post', function($postId)  {
    global $post;

    $fields = recurse(get_fields($postId));

    update_post_meta($postId, 'serialized_data', json_encode(
        array_merge($post->to_array(), $fields)
    ));
});