Check if CTP category taxonomy is set in shortcode

As already said in comments, do not ever use extract(). It’s use was removed from core a few years ago. The codex was also cleaned up. The use of extract() is even discouraged in PHP. You can rewrite your attributes as follow: (NOTE: Requires PHP 5.4+)

$attributes = shortcode_atts( 
    [
        'posts'    => -1,
        'order'    => '',
        'orderby'  => '',
        'title'    => '',
        'id'       => '',
        'category' => ''
    ],
    $atts 
);

To access an attribute, you can use the following syntax (which is normal array syntax)

$attribute['category']

If you are going to make use of the 'taxonomy' => 'term' syntax, you need to make sure your term is passed a slug. Also, when you register your taxonomy, you need to make sure that the query_var argument is left at default. I would rather suggest in using a proper tax_query which is more flexible.

In the example below, I have set a field attribute which is set to slug by default. This means that category can be a string or an array of slugs. If you need to pass term ID’s as an integer, string or an array, you can just pass term_id as value to the field attribute

add_shortcode( 'faqs', 'cpt_sc' );
function cpt_sc($atts) {

    $attributes = shortcode_atts( 
        [
            'posts'    => -1,
            'order'    => '',
            'orderby'  => '',
            'title'    => '',
            'id'       => '',
            'category' => ''
            'field'    => 'slug'
        ],
        $atts 
    );

    // Set up your variables
    $posts    = $attribute['posts'];
    $order    = $attribute['order'];
    $orderby  = $attribute['orderby'];
    $id       = $attribute['id'];
    $category = $attribute['category'];
    $field    = $attribute['field'];

    $args = [
        'post_type'      => 'cpt_faqs',
        'posts_per_page' => $posts,
        'order'          => $order,
        'orderby'        => $orderby,
        'p'              => $id,
        'tax_query'      => [
            [
                'taxonomy' => 'cpt_cats',
                'field'    => $field,
                'terms'    => $category
            ]
        ]
    ]; 

    // Rest of your shortcode

}

FEW NOTES

  • The code needs at least PHP 5.4. You should however be on PHP 5.6 already to be safe

  • To make the code more dynamic, you can always set attributes for the post type and taxonomy

  • It is always advice-able to escape, sanitize and/or validate any user passed data according to the data type expected. Because you are passing all attributes directly to WP_Query, you should be save if you do not sanitize/escape/validate as WP_Query will take care of that before the SQL query is executed. Still, it is good and recommended to always escape, sanitize and/or validate

EDIT

I actually forgot the handle how to check if a term is passed. You can check if a term is passed, and if the value is empty, you can simply bail or return an error message

Check with attribute

if ( '' == $attribute['category'] )
    return;

Check when the variables is set

if ( '' == $category )
    return;

EDIT – From comments

If a category is not specified in the shortcode, no results are returned. How can I make all posts return if no categroy is set?

We can display all posts regardless if no term is set by conditionally adding our tax_query. If a term is passed, add the tax_query, else, do not append the tax_query

We can try the following

$args = [
    'post_type'      => 'cpt_faqs',
    'posts_per_page' => $posts,
    'order'          => $order,
    'orderby'        => $orderby,
    'p'              => $id,
];

if ( $category ) {
    $args['tax_query'] = [
        [
            'taxonomy' => 'cpt_cats',
            'field'    => $field,
            'terms'    => $category
        ]
    ];
}