How I check if the same post slug has not been used before publishing?

For an indie rock agenda I created event posts. With the help of javascript, the custom fields values (bands, places and date info picked in select boxes) are copied as the post title when publishing or updating the post.There is also a custom function which update the slug every time the title changes.
Problem: if an other user create a new post picking exactly the same custom fields values, then an other post with exactly the same slug is created.
And if two posts with the same slug are created, both of them lead to a 404 error page until one of them is manually deleted.
My question: is there a way to “validate” the post slug comparing it to the older posts in the database ?
Thanks for your help !

Related posts

Leave a Reply

4 comments

  1. The Ajax way (a little bit dirty, but you can easily enhance it)

    In your functions.php (or in a plugin, or every where else) :

    function my_ajax_update(){
        if (isset($_POST['title'])){
            global $wpdb;
            $title = esc_sql($_POST['title']);
            if(!$title) return;
            $page = $wpdb->get_results("
                SELECT *
                FROM $wpdb->posts
                WHERE post_title LIKE '$title'
                AND post_type = 'event'
                AND post_status = 'publish'
                LIMIT 1
            ");
            if ($page) echo "This title already exists !";
        }
    }
    add_action("wp_ajax_check_double_post", "my_ajax_update");
    

    And somewhere in your metabox template (or loaded via an action) :

    jQuery(document).ready(function($){
        var the_title = $('#title').val();
        $('#publish').click(function(e){
            e.preventDefault();
            $.post(
                ajaxurl,
                {
                    action:"check_double_post",
                    title: the_title
                },
                function(data){
                    if ((data) != 0) {
                        message = $('<div id="message" class="error below-h2" />').html('<p>Warning, this title already exists!</p>')
                        $('h2').after(message);
                        $('#ajax-loading').hide();
                        $('#publish').removeClass('button-primary-disabled');
                    }
                    else {
                        $(this).submit();
                    }
                }
            );
        });
    });
    
  2. You’d be surprised at how tricky this one turns out to be. Closest I’m getting is hooking into the save_post action. There appears to be no action to hook to that will stop the post from saving, all the actions for update_post() and write_post() don’t appear to allow any direct interference; so the very simple and quick option I’ve come up with involves dying inside the hook. Even so, the post is saved with the duplicate name regardless of our dying.

    function check_post_title( $pid ) {
        $post = get_post( $pid );
        $events = get_posts( 's='.$post->post_title.'&post_type=event' );
    
        foreach( (array)$events as $event ) {
            if ( $event->ID == $post->ID ) continue;
            if ( $event->post_title == $post->post_title ) die('Oh noes! A post with this title already exits, go back and change it, please.');
        }
    }
    
    add_action( 'save_post', 'check_post_title' );
    

    The save_post action supplies the hook with the post ID of the saved post. Then events are searched for using the ‘s’ argument, just like you would search for using the regular WordPress search on your pages. The events are then iterated through and if a post with an ID other than the checked-upon post is found with a title that matches this checked-upon post then it dies.

    Something more helpful and less stripped down would involve a custom message when the post is published.

    function check_post_title( $pid ) {
        $post = get_post( $pid );
        $events = get_posts( 's='.$post->post_title.'&post_type=event' );
    
        foreach( (array)$events as $event ) {
            if ( $event->ID == $post->ID ) continue;
            if ( $event->post_title == $post->post_title ) {
                add_filter( 'redirect_post_location', 'event_exists' );
            }
        }
    }
    
    /* Alter the message */
    function event_exists( $redirect_url ) {
        $messages = array( 'message=1', 'message=2' );
        return str_replace( $messages, 'message=100', $redirect_url );
    }
    
    /* Add a custom event message */
    function custom_event_message( $messages ) {
        $messages['post']['100'] = 'Oh noes! An event with this name already exists, so go ahead and pick another.';
        return $messages;
    }
    
    add_filter( 'post_updated_messages', 'custom_event_message' );
    
    add_action( 'save_post', 'check_post_title' );
    

    This is a quick solution, something even more involved would perhaps require that the saved post which has just infringed the no-duplicate-title rule has to have it’s title modified and resaved by the function, turning it into something like ‘DUPLICATE! [the title here]’ and changing post status back to ‘pending’.

    The yellow message seems to be quite subtle and may not be noticed by publishers, dying seems to be more attention-grabbing.

    Hope this helps and didn’t confuse you too much. There may be better solutions to the problem, let’s hope someone comes by and helps us improve the solution. Any ideas will be appreciated at this point.

  3. Instead of making your own query, why not just using the function post_exists() ?
    It takes the post_title, content and date ( optionnal ).
    This function does not test the post_type but its rare that you have the same post_title on your content.

    Regards,
    Rahe.

  4. find if POST Slug already exists:

    $title = 'mytitlee';
    
    global $wpdb;
    $id_ofpost_name = $wpdb->get_var("SELECT ID FROM $wpdb->posts WHERE post_name = '$title'");
    $id_ofpost_title = $wpdb->get_var("SELECT ID FROM $wpdb->posts WHERE post_title = '$title'");
    if ($id_ofpost_name || $id_ofpost_title) {echo 'Existssss!';}