LLMS_Product
LLMS_Product model class.
Description Description
Both Courses and Memberships are sellable and can be instantiated as a product.
Source Source
File: includes/models/model.llms.product.php
class LLMS_Product extends LLMS_Post_Model { /** * Model properties. * * @var array */ protected $properties = array(); /** * Model DB Post Type. * * @todo The post type depends conditionally on whether it's a course or a membership so this is semantically incorrect. * * @var string */ protected $db_post_type = 'product'; /** * Model type. * * @var string */ protected $model_post_type = 'product'; /** * Retrieve the max number of access plans that can be created for this product. * * @since 3.0.0 * * @return int */ public function get_access_plan_limit() { /** * Determine the number of access plans allowed on the product. * * This is a (somewhat) arbitrary limit chosen mostly based on the following 2 factors: * * 1) It looks visually unappealing to have a pricing table with 7+ items on it. * 2) Having 7+ pricing plans creates a lot of decision fatigue for users. * * If you disagree with either of these two factors you can quite easily change the * limit using this filter. * * Keep in mind that increasing the limit will likely require you to add CSS to accommodate * 7+ plans on the automatically generated pricing tables. * * Also, since plans are limited by the core to 6, we have no pagination built in for any queries that * lookup or list access plans. This means that if you greatly increase the limit (say 200) you * could very quickly run into issues where the default queries "do not scale" well. In which * case you should first consider if you really need 200 plans and then start investigating other * filters to add pagination (and probably caching) to these (now slow) queries. * * @since 3.0.0 * * @param int $limit Number of plans. * @param LLMS_Proudct $this Product object. */ return apply_filters( 'llms_get_product_access_plan_limit', 6, $this ); } /** * Get all access plans for the product. * * @since 3.0.0 * @since 3.25.2 Unknown. * @since 3.37.17 Fixed a typo in the `post_status` query arg when retrieving access plans for this product. * * @param bool $free_only Optional. Only include free access plans if `true`. Defalt `false` * @param bool $visible_only Optional. Excludes hidden access plans from results. Default `true`. * @return array */ public function get_access_plans( $free_only = false, $visible_only = true ) { $args = array( 'meta_key' => '_llms_product_id', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key 'meta_value' => $this->get( 'id' ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value 'order' => 'ASC', 'orderby' => 'menu_order', 'posts_per_page' => $this->get_access_plan_limit(), 'post_type' => 'llms_access_plan', 'post_status' => 'publish', ); // Filter results to only free access plans. if ( $free_only ) { $args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query array( 'key' => '_llms_is_free', 'value' => 'yes', ), ); } // Exclude hidden access plans from the results. if ( $visible_only ) { $args['tax_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query array( 'field' => 'name', 'operator' => 'NOT IN', 'terms' => array( 'hidden' ), 'taxonomy' => 'llms_access_plan_visibility', ), ); } /** * Filter the product's access plan query args. * * @since Unknown * * @param array $args Query args. * @param LLMS_Product $product The LLMS_Product instance. * @param bool $free_only Whether or not to include the free access plans only. * @param bool $visbile_only Whether or not to exclude the hidden access plans. */ $query = new WP_Query( apply_filters( 'llms_get_product_access_plans_args', $args, $this, $free_only, $visible_only ) ); $plans = array(); // If we have plans, setup access plan instances. if ( $query->have_posts() ) { foreach ( $query->posts as $post ) { $plans[] = new LLMS_Access_Plan( $post ); } } /** * Filter the product's access plans. * * @since Unknown * * @param array $plans An array of LLMS_Access_Plan instances related to the product `$product`. * @param LLMS_Product $product The LLMS_Product instance. * @param bool $free_only Whether or not to include the free access plans only. * @param bool $visbile_only Whether or not to exclude the hidden access plans. */ return apply_filters( 'llms_get_product_access_plans', $plans, $this, $free_only, $visible_only ); } /** * Retrieve the product's catalog visibility term. * * @since 3.6.0 * * @return string */ public function get_catalog_visibility() { $terms = wp_get_post_terms( $this->get( 'id' ), 'llms_product_visibility' ); if ( $terms && is_array( $terms ) ) { $obj = $terms[0]; if ( isset( $obj->name ) ) { return $obj->name; } } return 'catalog_search'; } /** * Retrieve the product's catalog visibility name for display. * * @since 3.6.0 * * @return string */ public function get_catalog_visibility_name() { $visibility = $this->get_catalog_visibility(); $options = llms_get_product_visibility_options(); if ( isset( $options[ $visibility ] ) ) { return $options[ $visibility ]; } return $visibility; } /** * Get the number of columns for the pricing table. * * @since 3.0.0 * * @param bool $free_only Optional. Only include free access plans if true. Default `false`. * @return int */ public function get_pricing_table_columns_count( $free_only = false ) { $count = count( $this->get_access_plans( $free_only ) ); switch ( $count ) { case 0: $cols = 1; break; case 6: $cols = 3; break; default: $cols = $count; } /** * Filter the number of columns of the product's pricing table. * * @since 3.0.0 * * @param int $cols The number of columns of the pricing table for the `$product`. * @param LLMS_Product $product The LLMS_Product instance. * @param int $count The number of access plans related to the product `$product`. * @param bool $free_only Whether or not to include the free access plans only. */ return apply_filters( 'llms_get_product_pricing_table_columns_count', $cols, $this, $count, $free_only ); } /** * Retrieve a list of restrictions on the product. * * Restrictions are used to in conjunction with "is_purchasable()" to * determine if purchase/enrollment should be allowed for a given product. * * Restrictions in the core currently only exist on courses: * + Enrollment time period. * + Student capacity. * * @since 3.38.0 * * @return string[] An array of strings describing the restrictions placed on the product. */ public function get_restrictions() { $restrictions = array(); if ( 'course' === $this->get( 'type' ) ) { $course = new LLMS_Course( $this->get( 'id' ) ); // Is the course enrollment period open? if ( ! $course->is_enrollment_open() ) { $restrictions[] = 'enrollment_period'; } // Does the course have capacity? if ( ! $course->has_capacity() ) { $restrictions[] = 'student_capacity'; } } /** * Filter the product's restrictions. * * @since 6.8.0 * * @param string[] $restrictions An array of strings describing the restrictions placed on the product. * @param LLMS_Product $product The LLMS_Product object. */ return apply_filters( 'llms_product_get_restrictions', $restrictions, $this ); } /** * Determine if the product has at least one free access plan. * * @since 3.0.0 * @since 3.25.2 Unknown. * * @return bool */ public function has_free_access_plan() { /** * Filter whether the product has free access plans. * * @since Unknown * @since 3.37.17 Added the `$product` param. * * @param bool $has_free_access_plan Whether the product `$product` has free access plans. * @param LLMS_Product $product The LLMS_Product instance. */ return apply_filters( 'llms_product_has_free_access_plan', ( 0 !== count( $this->get_access_plans( true ) ) ), $this ); } /** * Determine if any restrictions exist on the product. * * @since 3.38.0 * * @see LLMS_Proudct::get_restrictions() * * @return boolean `true` if there is at least one restriction on the product, `false` otherwise. */ public function has_restrictions() { $restrictions = $this->get_restrictions(); $has_restrictions = count( $restrictions ) > 0; /** * Filter whether the product has any purchase restrictions. * * @since 3.38.0 * * @param bool $has_restrictions Whether the product `$product` has restrictions. * @param string[] $restrictions Array of restrictions placed on the product. * @param LLMS_Product $product The LLMS_Product object. */ return apply_filters( 'llms_product_has_restrictions', $has_restrictions, $restrictions, $this ); } /** * Determine if the product is purchasable. * * At least one gateway must be enabled and at least one access plan must exist. * If the product is a course, additionally checks to ensure course enrollment is open and has capacity. * * @since 3.0.0 * @since 3.25.2 Unknown. * @since 3.38.0 Use `has_restrictions()` to determine if the product has additional restrictions. * * @return bool */ public function is_purchasable() { // Default to false. $purchasable = false; // If the product doesn't have any purchase restrictions, make sure we have a purchasable plan & active gateways. if ( ! $this->has_restrictions() ) { $gateways = llms()->payment_gateways(); $purchasable = ( $this->get_access_plans( false, false ) && $gateways->has_gateways( true ) ); } /** * Filter whether the product is purchasable. * * @since Unknown * * @param bool $purchasable Whether the product `$product` is purchasable. * @param LLMS_Product $product The LLMS_Product instance. */ return apply_filters( 'llms_product_is_purchasable', $purchasable, $this ); } /** * Update the product's catalog visibility setting. * * @since 3.6.0 * @since 3.37.17 Use `in_array` with strict comparison. * * @param string $visibility Visibility term name. * @return void */ public function set_catalog_visibility( $visibility ) { if ( ! in_array( $visibility, array_keys( llms_get_product_visibility_options() ), true ) ) { return; } wp_set_object_terms( $this->get( 'id' ), $visibility, 'llms_product_visibility', false ); } /** * Check if there are active subscriptions for this product. * * @since 5.4.0 * * @param boolean $use_cache Whether or not leveraging the cache. * @return boolean */ public function has_active_subscriptions( $use_cache = true ) { $found = false; if ( $use_cache ) { $subscriptions_count = wp_cache_get( $this->get( 'id' ), 'llms_product_subscriptions_count', true, $found ); } if ( false === $found ) { global $wpdb; $subscriptions_count = $wpdb->get_var( $wpdb->prepare( " SELECT COUNT(*) FROM {$wpdb->posts} as p JOIN {$wpdb->postmeta} as pm1 JOIN {$wpdb->postmeta} as pm2 WHERE p.ID=pm1.post_id AND p.post_type='llms_order' AND pm1.post_id=pm2.post_id AND pm1.meta_key='_llms_product_id' AND pm1.meta_value=%d AND pm2.meta_key='_llms_order_type' AND pm2.meta_value='recurring' AND p.post_status IN ( 'llms-active', 'llms-pending-cancel', 'llms-on-hold' ) ", $this->get( 'id' ) ) ); wp_cache_set( $this->get( 'id' ), $subscriptions_count, 'llms_product_subscriptions_count' ); } return (bool) $subscriptions_count; } }
Expand full source code Collapse full source code View on GitHub
Methods Methods
- get_access_plan_limit — Retrieve the max number of access plans that can be created for this product.
- get_access_plans — Get all access plans for the product.
- get_catalog_visibility — Retrieve the product's catalog visibility term.
- get_catalog_visibility_name — Retrieve the product's catalog visibility name for display.
- get_pricing_table_columns_count — Get the number of columns for the pricing table.
- get_restrictions — Retrieve a list of restrictions on the product.
- has_active_subscriptions — Check if there are active subscriptions for this product.
- has_free_access_plan — Determine if the product has at least one free access plan.
- has_restrictions — Determine if any restrictions exist on the product.
- is_purchasable — Determine if the product is purchasable.
- set_catalog_visibility — Update the product's catalog visibility setting.
Changelog Changelog
Version | Description |
---|---|
3.38.0 | Add get_restrictions() and has_restrictions() methods. |
3.37.17 | Fixed a typo in the post_status query arg when retrieving access plans for this product. Use in_array with strict comparison where possible. |
3.25.2 | Unknown. |
1.0.0 | Introduced. |