-
Notifications
You must be signed in to change notification settings - Fork 10
Refactor: Component based PHP rendering #673
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
abhishekxix
wants to merge
26
commits into
theme-elementary-v2
Choose a base branch
from
refactor/component-based-php-rendering
base: theme-elementary-v2
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 6 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
dc092e3
Add Component based rendering with priority
abhishekxix fd4b542
Add output buffered get static method
abhishekxix c60aa58
Add convenience wrapper for get method
abhishekxix 4512e3a
Fix global variable prefix phpcs error
abhishekxix 223b9ab
Fix WP global phpcs errors
abhishekxix 961f624
Fix short ternary error
abhishekxix 18b7098
Address feedback
abhishekxix 8641cd9
Merge branch 'theme-elementary-v2' into refactor/component-based-php-…
Adi-ty 9b3ae81
feat: add enqueue assets filter and action that can be reused.
bhavz-10 a4feb94
feat: add assets enqeue by default for the components
bhavz-10 beb73b1
feat: optimize, add cache loader for storing the paths statically
bhavz-10 95e33c4
feat: add theme and plugin component functions for assets and php par…
bhavz-10 28da679
feat: add js, scss and configure webpack
bhavz-10 333b541
feat: add card css
bhavz-10 fb50d2c
Update component source path casing
bhavz-10 e3384fd
Lowercase component source files
bhavz-10 5f47b4c
Refactor ComponentLoader: optimize assets, add type safety, and clean…
bhavz-10 c294c98
Merge branch 'theme-elementary-v2' of github.com:rtCamp/theme-element…
bhavz-10 2931ff5
refactor: remove ComponentLoader and its test
bhavz-10 861f4ae
fix: update webpack config js
bhavz-10 154dbbf
tests: update webpack tests
bhavz-10 bbd6245
refactor: change the Component loader usage
bhavz-10 022d4d1
Implement shared ThemeComponentLoader via bootstrap container
bhavz-10 8d9247e
Fix PHPCS missing short description error for inline doc comment
bhavz-10 e8e3040
Fix JS linting errors in button and card components
bhavz-10 0adc15a
Ignore PHP files in Stylelint configuration to fix CSS linting errors
bhavz-10 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,189 @@ | ||
| <?php | ||
| /** | ||
| * Component loader for resolving and rendering PHP component partials. | ||
| * | ||
| * Resolves components from theme or plugin paths with configurable priority. | ||
| * Components are render-only PHP files that receive data as arguments and output HTML. | ||
| * | ||
| * @package rtCamp\Theme\Elementary | ||
| */ | ||
|
|
||
| declare( strict_types = 1 ); | ||
|
|
||
| namespace rtCamp\Theme\Elementary\Framework; | ||
|
|
||
| /** | ||
| * Class ComponentLoader | ||
| * | ||
| * @since 1.0.0 | ||
| */ | ||
| class ComponentLoader { | ||
|
|
||
| /** | ||
| * Render a component by name. | ||
| * | ||
| * Resolves the component file based on registered paths and priority, | ||
| * then includes it with the provided arguments available in scope. | ||
| * | ||
| * @param string $name Component name (e.g. 'Button', 'Card'). | ||
| * @param array $args Arguments to pass to the component. | ||
| * @param array $options { | ||
| * Optional. Resolution options. | ||
| * | ||
| * @type string $priority Resolution priority: 'theme' or 'plugin'. Default determined by filter. | ||
| * } | ||
| * | ||
| * @return void | ||
| */ | ||
| public static function render( string $name, array $args = [], array $options = [] ): void { | ||
|
|
||
| $file = self::get_component_file( $name, $options ); | ||
|
|
||
| if ( false === $file ) { | ||
| return; | ||
| } | ||
|
|
||
| require $file; | ||
| } | ||
|
|
||
| /** | ||
| * Get the rendered HTML of a component as a string. | ||
| * | ||
| * Uses output buffering to capture the component output instead of | ||
| * sending it directly to the browser. | ||
| * | ||
| * @param string $name Component name (e.g. 'Button', 'Card'). | ||
| * @param array $args Arguments to pass to the component. | ||
| * @param array $options { | ||
| * Optional. Resolution options. | ||
| * | ||
| * @type string $priority Resolution priority: 'theme' or 'plugin'. Default determined by filter. | ||
| * } | ||
| * | ||
| * @return string Rendered component HTML, or empty string if not found. | ||
| */ | ||
| public static function get( string $name, array $args = [], array $options = [] ): string { | ||
|
|
||
| ob_start(); | ||
| self::render( $name, $args, $options ); | ||
|
|
||
| return (string) ob_get_clean(); | ||
| } | ||
|
|
||
| /** | ||
| * Resolve the component file path. | ||
| * | ||
| * Checks registered paths in priority order and returns the first match. | ||
| * Path format: {source_path}/{Name}/{Name}.php | ||
| * | ||
| * @param string $name Component name. | ||
| * @param array $options Resolution options. | ||
| * | ||
| * @return string|false Full file path on success, false if not found. | ||
| */ | ||
| private static function get_component_file( string $name, array $options = [] ): string|false { | ||
|
|
||
| $priority = self::get_priority( $options ); | ||
|
|
||
| /** | ||
| * Filters the registered component paths. | ||
| * | ||
| * Each entry is keyed by source type ('theme', 'plugin') and maps | ||
| * to a directory path where components are stored. | ||
| * | ||
| * @since 1.0.0 | ||
| * | ||
| * @param array $paths Associative array of source => directory path. | ||
| * @param string $name Component name being resolved. | ||
| * @param array $options Options passed to render(). | ||
| */ | ||
| $paths = apply_filters( | ||
| 'elementary_theme_component_paths', | ||
| [ | ||
| 'theme' => ELEMENTARY_THEME_TEMP_DIR . '/src/Components', | ||
| ], | ||
| $name, | ||
| $options | ||
| ); | ||
|
|
||
| // Order sources based on priority. | ||
| $order = self::get_source_order( $priority, $paths ); | ||
|
|
||
|
|
||
| foreach ( $order as $source ) { | ||
|
|
||
| if ( empty( $paths[ $source ] ) ) { | ||
| continue; | ||
| } | ||
|
|
||
| $file = trailingslashit( $paths[ $source ] ) . $name . '/' . $name . '.php'; | ||
|
|
||
| if ( file_exists( $file ) && is_readable( $file ) ) { | ||
| return $file; | ||
| } | ||
|
|
||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| /** | ||
| * Get the resolution priority. | ||
| * | ||
| * @param array $options Options array potentially containing 'priority'. | ||
| * | ||
| * @return string 'theme' or 'plugin'. | ||
| */ | ||
| private static function get_priority( array $options ): string { | ||
|
|
||
| if ( ! empty( $options['priority'] ) && in_array( $options['priority'], [ 'theme', 'plugin' ], true ) ) { | ||
| return $options['priority']; | ||
| } | ||
|
|
||
| /** | ||
| * Filters the default component resolution priority. | ||
| * | ||
| * @since 1.0.0 | ||
| * | ||
| * @param string $priority Default priority. Accepts 'theme' or 'plugin'. | ||
| */ | ||
| $default = apply_filters( 'elementary_theme_component_default_priority', 'theme' ); | ||
|
|
||
| if ( in_array( $default, [ 'theme', 'plugin' ], true ) ) { | ||
| return $default; | ||
| } | ||
|
|
||
| return 'theme'; | ||
| } | ||
|
|
||
| /** | ||
| * Get the source resolution order based on priority. | ||
| * | ||
| * @param string $priority 'theme' or 'plugin'. | ||
| * @param array $paths Registered paths keyed by source. | ||
| * | ||
| * @return array Ordered list of source keys to check. | ||
| */ | ||
| private static function get_source_order( string $priority, array $paths ): array { | ||
|
|
||
| $sources = array_keys( $paths ); | ||
|
|
||
| if ( 'plugin' === $priority ) { | ||
| // Move 'plugin' to front if it exists. | ||
| $key = array_search( 'plugin', $sources, true ); | ||
|
|
||
| if ( false !== $key ) { | ||
| unset( $sources[ $key ] ); | ||
| array_unshift( $sources, 'plugin' ); | ||
| } | ||
| } else { | ||
| // Move 'theme' to front if it exists. | ||
| $key = array_search( 'theme', $sources, true ); | ||
|
|
||
| if ( false !== $key ) { | ||
| unset( $sources[ $key ] ); | ||
| array_unshift( $sources, 'theme' ); | ||
| } | ||
| } | ||
|
|
||
| return array_values( $sources ); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
aryanjasala marked this conversation as resolved.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| <?php | ||
| /** | ||
| * Button component. | ||
| * | ||
| * A render-only component that outputs a button or link element. | ||
| * | ||
| * @package rtCamp\Theme\Elementary | ||
| * | ||
| * @param array $args { | ||
| * Component arguments. | ||
| * | ||
| * @type string $label Button label text. Required. | ||
| * @type string $url URL for link buttons. Optional. | ||
| * @type string $class Additional CSS classes. Optional. | ||
| * @type string $tag HTML tag: 'a' or 'button'. Optional. Defaults to 'a' when $url is set, 'button' otherwise. | ||
| * } | ||
| */ | ||
|
|
||
| $label = $args['label'] ?? ''; | ||
| $url = $args['url'] ?? ''; | ||
| $class = $args['class'] ?? ''; | ||
| $tag = $args['tag'] ?? ( ! empty( $url ) ? 'a' : 'button' ); | ||
|
|
||
| if ( empty( $label ) ) { | ||
| return; | ||
| } | ||
|
|
||
| $css_class = trim( 'elementary-button ' . $class ); | ||
|
|
||
| if ( 'a' === $tag && ! empty( $url ) ) { | ||
| printf( | ||
| '<a href="%s" class="%s">%s</a>', | ||
| esc_url( $url ), | ||
| esc_attr( $css_class ), | ||
| esc_html( $label ) | ||
| ); | ||
| } else { | ||
| printf( | ||
| '<button type="button" class="%s">%s</button>', | ||
| esc_attr( $css_class ), | ||
| esc_html( $label ) | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| <?php | ||
| /** | ||
| * Card component. | ||
| * | ||
| * A render-only component that outputs a card with optional image, title, | ||
| * description, and action button. Demonstrates component composability | ||
| * by rendering the Button component internally. | ||
| * | ||
| * @package rtCamp\Theme\Elementary | ||
| * | ||
| * @param array $args { | ||
| * Component arguments. | ||
| * | ||
| * @type string $title Card title. Required. | ||
| * @type string $description Card description text. Optional. | ||
| * @type string $image_url Card image URL. Optional. | ||
| * @type string $url Card link URL. Optional. | ||
| * } | ||
| */ | ||
|
|
||
| use rtCamp\Theme\Elementary\Framework\ComponentLoader; | ||
|
|
||
| $title = $args['title'] ?? ''; | ||
| $description = $args['description'] ?? ''; | ||
| $image_url = $args['image_url'] ?? ''; | ||
| $url = $args['url'] ?? ''; | ||
|
|
||
| if ( empty( $title ) ) { | ||
| return; | ||
| } | ||
|
|
||
| ?> | ||
| <div class="elementary-card"> | ||
| <?php if ( ! empty( $image_url ) ) : ?> | ||
| <div class="elementary-card__image"> | ||
| <img src="<?php echo esc_url( $image_url ); ?>" alt="<?php echo esc_attr( $title ); ?>" /> | ||
| </div> | ||
| <?php endif; ?> | ||
|
|
||
| <div class="elementary-card__content"> | ||
| <h3 class="elementary-card__title"><?php echo esc_html( $title ); ?></h3> | ||
|
|
||
| <?php if ( ! empty( $description ) ) : ?> | ||
| <p class="elementary-card__description"><?php echo esc_html( $description ); ?></p> | ||
| <?php endif; ?> | ||
|
|
||
| <?php if ( ! empty( $url ) ) : ?> | ||
| <div class="elementary-card__action"> | ||
| <?php | ||
| ComponentLoader::render( | ||
| 'Button', | ||
| [ | ||
| 'label' => $title, | ||
| 'url' => $url, | ||
| 'class' => 'elementary-card__button', | ||
| ] | ||
| ); | ||
| ?> | ||
| </div> | ||
| <?php endif; ?> | ||
| </div> | ||
| </div> | ||
| <?php |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the new refactor, we might want to make this class a shared instance, and during initiation we might want to set the default component directory in a variable, so that it doesn't need to be added again and again later - as this would be inside framework now and framework would be added via composer.