• I’m a little stuck on the following use case, maybe someone here could give me a nudge in the right direction…

    I’ve created a custom post type called Products. Inside the admin products can be created with the usual parts like featured image, a title and content. I display these products on the frontend via a template.
    On the same frontend I display a Contact Form 7 form where visitors can make a bid on one of the products. After form submit a second custom post type with the name of Offers is created. Inside the admin I can view who made an offer on which product and what the amount of the offer is.

    Now, I want to display the highest offer inside each product. Maybe a written-out layout explains it a bit better. I’ve created a for/while loop which loops through the products and per product creates a div with:
    [product title]
    [product content: image & some text]
    [highest offer: offer data from product] < how to get this data?

    I’m a little at a loss on how to accomplish this or if it’s even possible.

    Any tips are appreciated.

    Following is the code of my template so far:

    <?php
    /** template name: homepage */
    get_header();
    ?>
    
    <div class="container">
        <div class="row">
            <div class="col-md-8">
            <?php
                $productArgs = array(
                    'post_type'			=> 'products',
                    'post_status'		=> 'publish',
                    'posts_per_page'	=> -1,
                    'order'             => 'ASC'
                );
                $products = new WP_Query($productArgs);
                if($products->have_posts()) {
                ?>
                    <div class="products">
                        <?php while($products->have_posts()) : $products->the_post(); 
                            $productImage = wp_get_attachment_url( get_post_thumbnail_id($post->ID) );
                        ?>
                            <div class="product">
                                <div class="row">
                                    <div class="col-md-3">
                                        <div class="product__image">
                                            <?php if($productImage) { ?>
                                                <div style="background-image: url('<?= $productImage ?>');"></div>
                                            <?php } else { ?>
                                                <div style="background-image: url(<?php echo get_template_directory_uri() . '-child/images/no-image.jpg' ?>);"></div>
                                            <?php } ?>
                                        </div>
                                    </div>
    
                                    <div class="col-md-9">
                                        <div class="product__content">
                                            <p class="product__title">
                                                <?= the_title(); ?>
                                            </p>
    
                                            <?= the_content(); ?>
    
                                            <div>
                                                Hoogste bod: 
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        <?php endwhile; wp_reset_postdata(); ?>
                    </div>
                <?php } else { ?>
                    Geen producten gevonden
                <?php } ?>
            </div>
    
            <div class="col-md-4">
                <?= do_shortcode( '[contact-form-7 id="15" title="Contact form 1"]' ); ?>
            </div>
        </div>
    </div>
    
    <?php
    get_footer();
    

    The page I need help with: [log in to see the link]

Viewing 15 replies - 1 through 15 (of 17 total)
  • Moderator bcworkz

    (@bcworkz)

    How is the offer amount stored with the offer post? If post meta, when you query for offers related to a particular product, order results descending by that post meta value. With that ordering, you only need the first post returned to get the highest offer. You can use get_posts() with appropriate arguments for this. Allowed arguments are the same as those for the WP_Query object.

    Thread Starter floi13

    (@floi13)

    Hi bcworkz,

    The Offers cpt is created with the following code:

    function cptOffers() {
    	$labels = array(
    		'name'					=> _x('Offers', 'post type general name'),
    		'singular_name'			=> _x('offer', 'post type singular name'),
    		//'add_new' 			=> _x('Add new', 'offer'),
    		//'add_new_item' 		=> __('Add new offer'),
    		'edit_item'				=> __('Information about this offer'),
    		//'new_item'			=> __('New offer'),
    		'all_items'				=> __('All offers'),
    		'view_item'				=> __('View offer'),
    		'search_items' 			=> __('Search offers'),
    		'not_found'				=> __('No offers found'),
    		'not_found_in_trash' 	=> __('No offers found in trash'),
    		'parent_item_colon' 	=> '',
    		'menu_name'				=> 'Offers'
    	);
    	
    	$args = array(
    		'labels'				=> $labels,
    		'description'			=> 'All offers',
    		'public'				=> true,
    		'menu_icon'				=> 'dashicons-money-alt',
    		'supports'				=> array('title', 'thumbnail', 'custom-fields'),
    		'has_archive'			=> true,
    		'map_meta_cap'			=> true,
    		'capabilities'			=> array(
    			'create_posts'	=> 'do_not_allow'
    		)
    	);
    	
    	register_post_type('offers', $args);
    }
    add_action('init', 'cptOffers', 0);

    The separate ‘posts’ are created via a form submit:

    add_action('wpcf7_before_send_mail', 'save_application_form');
    function save_application_form($wpcf7){
    	global $wpdb;
      	$submission = WPCF7_Submission::get_instance();
    
      	$name 			= $_POST['your-name'];
      	$email 			= $_POST['email'];
      	$productchoice 	= $_POST['radio-products'];
      	$offer 			= $_POST['offer'];
      	$comments 		= $_POST['message'];
    
      	$my_post = array(
        	'post_title' => $name,
        	'post_status' => 'publish',
        	'post_type' => 'offers',
        	'post_author' => 1,
      	);
      
    	$the_post_id = wp_insert_post($my_post);
      
    	update_post_meta($the_post_id, 'offer_made', $_POST['offer']);
    	update_post_meta($the_post_id, 'chosen_product', $_POST['products']);
    	update_post_meta($the_post_id, 'user_comments', $_POST['message']);
    }

    Every Offer post has 3 advanced custom fields, where ‘offer_made’ is the amount the user entered in the form.

    Thread Starter floi13

    (@floi13)

    The thing that makes it a bit strange for me is that in my current setup I have two different custom post types. Reading up on meta data, the articles I’ve read so far all talk about the same post type…

    Thread Starter floi13

    (@floi13)

    I thought I had figured it out by getting the Offers cpt data via the REST api, but that didn’t work either. The url to the data I had in mind was something like //mydomain.test/wp-json/wp/v2/cpt-slug/$post->ID, but that would get the offer id; not the product id… So again two different items which should be combined.

    I’m thinking more and more what I’m trying to achieve can’t be done.

    Moderator bcworkz

    (@bcworkz)

    Isn’t there a custom field or meta value relating the offer to the product? Qualify each query for an offer by the current product so you end up with the best offer on that one product. Querying with two different meta values while ordering by one of them is a bit tricky. Use the “meta_query” arg for get_posts() or a new WP_Query class to limit results by product. Pass a “meta_key” argument for the field you want to order by. Don’t specify a “meta_value” arg because this field isn’t for restricting results, only ordering.

    Thread Starter floi13

    (@floi13)

    Thanks for your reply, @bcworkz
    Like you said I think the biggest problem I have is that there is no link between the cpts Offers and Products. Will have a look at that ‘meta_query’ and ‘meta_key’ you suggested.

    Thread Starter floi13

    (@floi13)

    So, I’ve added a new acf Relationship field to the Product(s) post type. That gives me a dropdown for every product. But the available offer-posts must be selected by hand; this is not what I want.

    Question #1
    Is a relationship-field the way to go?

    Question #2
    If the answer on question #1 is yes, how do I proceed? Perhaps populating the dropdown with corresponding offers?

    Moderator bcworkz

    (@bcworkz)

    Post meta would normally be the way to relate two post types. However, because offers are clearly subordinate to products, yet are non-hierarchical, you could utilize the post_parent property of offer posts to keep track of the product they relate to. This is how attachment posts relate to their related blog post. It’ll make related queries easier and faster.

    Having offerers manually select the parent/product is far from ideal. Some will accidentally select the wrong product. It should be kept track of automatically in code. From whatever link invokes an offer creation, there ought to be a parameter indicating the parent ID. The parameter is used to populate a read-only or hidden field of the offer form. That field value then becomes the post_parent property when the offer is saved.

    Thread Starter floi13

    (@floi13)

    Could you provide a code example or maybe a snippet on which I can build upon?
    I tried finding it via Google, but I’m a little lost..

    Moderator bcworkz

    (@bcworkz)

    Because it relates to how offers are made, it’d be difficult to give a meaningful example. I’ve no idea how offers happen on you site. Somehow, the product ID needs to be in a field named “post_parent” on the form users make offers from. With that name, when WP processes the form data, it should populate the WP_Post offer object property automatically.

    There’s a chance it won’t if offers are non-hierarchical. If it’s not done automatically, use the ‘wp_insert_post_data’ filter to set it yourself from $_POST[‘post_parent’].

    Thread Starter floi13

    (@floi13)

    The custom post type Offers I made in functions.php with the following code:

    function cptOffers() {
    	$labels = array(
    		'name' => _x('Offers', 'post type general name'),
    		'singular_name' => _x('offer', 'post type singular name'),
    		'edit_item' => __('Information about this offer'),
    		'all_items' => __('All offers'),
    		'view_item' => __('View offer'),
    		'search_items' => __('Search offers'),
    		'not_found' => __('No offers found'),
    		'not_found_in_trash' => __('No offers found in trash'),
    		'parent_item_colon' => '',
    		'menu_name' => 'Offers',
    	);
    	
    	$args = array(
    		'labels' => $labels,
    		'description' => 'All offers',
                    'public' => true,
                    'show_in_rest' => true,
    		'menu_icon' => 'dashicons-money-alt',
    		'supports' => array('title', 'thumbnail', 'custom-fields'),
    		'map_meta_cap' => true,
    		'capabilities' => array(
    			'create_posts'	=> 'do_not_allow'
    		),
    	);
    	
    	register_post_type('offers', $args);
    }
    add_action('init', 'cptOffers', 0);

    The ‘offer’ posts are create through form submit, with this piece of code:

    add_action('wpcf7_before_send_mail', 'save_application_form');
    function save_application_form($wpcf7){
    	global $wpdb;
      	$submission = WPCF7_Submission::get_instance();
    
      	$name 			= $_POST['your-name'];
      	$email 			= $_POST['email'];
      	$productchoice 	        = $_POST['radio-products'];
      	$offer 			= $_POST['offer'];
      	$comments 		= $_POST['message'];
    
      	$my_post = array(
        	'post_title' => $name,
        	'post_status' => 'publish',
        	'post_type' => 'offers',
        	'post_author' => 1,
      	);
      
    	$the_post_id = wp_insert_post($my_post);
      
    	update_post_meta($the_post_id, 'offer_made', $_POST['offer']);
    	update_post_meta($the_post_id, 'chosen_product', $_POST['products']);
    	update_post_meta($the_post_id, 'user_comments', $_POST['message']);
    }

    Correct me if I’m wrong, but I should add a hidden field to my form that holds the product id?

    Thread Starter floi13

    (@floi13)

    If you want I could give you the url and login if that would help clear some stuff? It’s just a development website, so no harm no foul.

    Moderator bcworkz

    (@bcworkz)

    Thank you for the offer, but forum guidelines are pretty adamant about not allowing members to log into others sites. I’ve no doubt there is little risk in this case, but none the less I need to decline your kind offer.

    If you are able to separate out the relevant code and post it publicly I’d be willing to take a look. If a lot of code is involved please use pastebin.com or gist.github.com and just drop the link here. I’m still not confident I’d be able to find useful code examples, but perhaps I could provide more specific guidance.

    Thread Starter floi13

    (@floi13)

    Ow, my bad. I’ve created a github where I added my template-home.php and functions.php. Would be great if you could take a peak.

    The link is https://gist.github.com/floi5314/ac0f782cb86aeb934907c317d8660400

    Moderator bcworkz

    (@bcworkz)

    Oh, I see now. That’s not what I had imagined. You have a single offer form below a list of products, requiring users to select a product. That strikes me as error prone. I’d think it’d be better to have a button after each product that links to an offer form, either on another page or as a modal. Whatever code launches that page or modal would pass the ID for the related product so the form code can have a field that contains that value.

    You can save the passed ID in post meta “chosen_product” as you do now, or include it in the wp_insert_post() data as the “post_parent” argument. (the code in functions.php starting at line 84) You can then query for the highest offer for any given product either way. Queries with the chosen product in post_parent are going to be more efficient than those with it in post meta.

    I think you’d need to make a new offer query for each product listed, so populating a full page of products with best offers isn’t going to be very efficient anyway. Any small gain in each offer query will be multiplied by the number of products.

    To be clear, you can get the best offer for any give product as things are now because offer posts do have their related product in “chosen_product” post meta. Query as I outlined in my second reply. At the time it wasn’t clear to me that there was a relation to products, but now I see there is. Users needing to select a product is a separate issue from how to query for best offers.

Viewing 15 replies - 1 through 15 (of 17 total)
  • The topic ‘Combine two custom post types’ is closed to new replies.