-
Notifications
You must be signed in to change notification settings - Fork 206
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
feat: Dokan Data Store #2346
base: develop
Are you sure you want to change the base?
feat: Dokan Data Store #2346
Changes from all commits
ea2a6e9
1628844
40e5ecb
a216c96
7e681f9
f583f2c
73b289d
c8b207c
aae536f
9b5aa1c
80b6bee
fc33dc8
a07efbf
afaeb93
be45417
a47f411
f57f2fa
7d4b345
d348e0c
45965a3
a8ce67d
59ea07f
d504acb
b950b9c
774843d
8417e49
cb6b364
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
## Dokan Model & Data Store Documentation | ||
|
||
- [Data Model](#data-model) | ||
- [Data Store](#data-store) | ||
- [Uses of Models](#uses-of-models) | ||
|
||
## Data Model | ||
|
||
### Overview | ||
The Dokan Data Model class should follow a structure similar to the `WC_Product` class, where the data layer is abstracted through the **Data Store**. This separation ensures that the model class is used throughout the Dokan plugin without interacting directly with the database or Data Store. | ||
|
||
### Goal | ||
The goal is to minimize the use of raw queries and enhance performance across various parts of the plugin. The data model should efficiently manage storing and retrieving data from the database. | ||
|
||
### Implementation | ||
The model class should extend the [\WeDevs\Dokan\Models\BaseModel](../includes/Models/BaseModel.php) class. It includes the following key properties and methods: | ||
|
||
#### Properties | ||
- `protected $object_type`: The type of object, such as `product`. | ||
- `protected $data`: Holds the default data for the object. | ||
|
||
#### Methods | ||
- The `protected $data_store` property is initialized in the constructor. | ||
- Getter and Setter methods use the `get_{key}` and `set_{key}` conventions, leveraging the `get_prop( $prop_name, $context )` and `set_prop( $prop_name, $prop_value )` methods for interacting with data properties. | ||
|
||
Below is a sample class implementation: | ||
|
||
```php | ||
class Department extends \WeDevs\Dokan\Models\BaseModel { | ||
protected $object_type = 'department'; | ||
|
||
protected $data = array( | ||
'name' => '', | ||
'date_created' => null, | ||
'date_updated' => null, | ||
); | ||
|
||
/** | ||
* Initialize the data store | ||
*/ | ||
public function __construct( int $item_id = 0 ) { | ||
parent::__construct( $item_id ); | ||
$this->data_store = new \WeDevs\Dokan\Models\DataStore\DepartmentStore(); | ||
|
||
if ( $this->get_id() > 0 ) { | ||
$this->data = $this->data_store->read( $this ); | ||
} | ||
} | ||
|
||
public function get_name( $context = 'view' ) { | ||
return $this->get_prop('name', $context); | ||
} | ||
|
||
public function set_name( $name ) { | ||
return $this->set_prop( 'name', $name ); | ||
} | ||
|
||
/** | ||
* @return \WC_DateTime|NULL Since the value was set by `set_date_prop` method | ||
*/ | ||
public function get_date_created( $context = 'view' ) { | ||
return $this->get_prop('date_created', $context); | ||
} | ||
|
||
/** | ||
* Set the date type value using the `set_date_prop` method. | ||
*/ | ||
public function set_date_created( $date_created ) { | ||
return $this->set_date_prop( 'date_created', $date_created ); | ||
} | ||
|
||
/** | ||
* @return \WC_DateTime|NULL Since the value was set by `set_date_prop` method | ||
*/ | ||
public function get_date_updated( $context = 'view' ) { | ||
return $this->get_prop('date_updated', $context); | ||
} | ||
|
||
public function set_date_updated( $date_updated ) { | ||
return $this->set_date_prop( 'date_updated', $date_updated ); | ||
} | ||
|
||
/** | ||
* Set the updated date for the entity. | ||
* | ||
* This method is protected to ensure that only internal code like `set_props` method | ||
* can set the updated date dynamically. External clients should use | ||
* the `set_date_created` and `set_date_updated` methods to manage dates semantically. | ||
* | ||
* @param string|int|DateTime $date_created | ||
*/ | ||
protected function set_updated_at( $date_updated ) { | ||
$this->set_date_updated( $date_updated ); | ||
} | ||
} | ||
``` | ||
|
||
## Data Store | ||
|
||
- [Override DB Date Format](#override-db-date-format) | ||
- [Customizing the ID Field](#customizing-the-id-field) | ||
|
||
Your data store class should extend the [\WeDevs\Dokan\Models\DataStore\BaseDataStore](../includes/Models/DataStore/BaseDataStore.php) class and must implement the following methods: | ||
|
||
- `get_table_name`: Defines the table name in the database. | ||
- `get_fields_with_format`: Returns the fields with format as an array where key is the db field name and value is the format.. | ||
|
||
Sample implementation: | ||
|
||
```php | ||
class DepartmentStore extends \WeDevs\Dokan\Models\DataStore\BaseDataStore { | ||
public function get_table_name(): string { | ||
return 'dokan_department'; | ||
} | ||
|
||
public function get_fields_with_format(): array { | ||
return [ | ||
'name' => '%s', | ||
'date_created' => '%s', | ||
'updated_at' => '%s', | ||
]; | ||
} | ||
|
||
public function get_date_updated( $context = 'view' ) { | ||
return $this->get_prop('date_updated', $context); | ||
} | ||
} | ||
``` | ||
|
||
### Override DB Date Format | ||
|
||
The Data Store first checks for the existence of the `get_{field_name}` method to map the data during insert or update operations. There are two ways to override the DB date format: | ||
|
||
**Option 1:** | ||
Override the `get_date_format_for_field` method to specify a custom format. | ||
```php | ||
protected function get_date_format_for_field( string $db_field_name ): string { | ||
return 'Y-m-d'; | ||
} | ||
``` | ||
|
||
**Option 2:** | ||
Create a custom method like `get_{db_field}` for more control over date formatting: | ||
```php | ||
protected function get_updated_at( Department $model, $context = 'edit' ): string { | ||
return $model->get_date_updated( $context )->date('Y-m-d'); | ||
} | ||
``` | ||
> **Option 2** could be used to map the data during insert or update operations when Model's data key and Store's data key are different. | ||
|
||
### Customizing the ID Field | ||
|
||
To customize the name and format of the ID field in the database, override the `get_id_field_name` and `get_id_field_format` methods. By default, the ID field is set to `id` and format is `%d`. | ||
|
||
## Uses of Models | ||
|
||
### Create a New Record | ||
```php | ||
$department = new Department(); | ||
$department->set_name('Department 1'); | ||
$department->save(); | ||
``` | ||
|
||
### Read a Record | ||
```php | ||
$department = new Department( $department_id ); | ||
echo $department->get_name(); | ||
``` | ||
|
||
### Update a Record | ||
```php | ||
$department = new Department( $department_id ); | ||
$department->set_name('Department 2'); | ||
$department->save(); | ||
``` | ||
|
||
### Case Study | ||
Pls check the example [get_particulars](../includes/Models/VendorBalance.php#L16) & [set_perticulars](../includes/Models/VendorBalance.php#L146) methods of Model and [get_perticulars](../includes/Models/DataStore/VendorBalanceStore.php#L35) method of Data Store. | ||
|
||
`perticulars` field name in database is *typo* but I don't want to expose public method `get_perticulars` in Model class instead of `get_particulars`. | ||
|
||
We could do the same thing by overriding the following methods in Data Store. | ||
- `protected function map_model_to_db_data( BaseModel &$model ): array`: Prepare data for saving a BaseModel to the database. | ||
- `map_db_raw_to_model_data`: Maps database raw data to model data. |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,38 @@ | ||||||||||||||||||||||||||||||||||||||||||||
<?php | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
namespace WeDevs\Dokan\DependencyManagement\Providers; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
use WeDevs\Dokan\DependencyManagement\BaseServiceProvider; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
class ModelServiceProvider extends BaseServiceProvider { | ||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||
* Tag for services added to the container. | ||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||
public const TAG = 'ajax-service'; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
protected $services = [ | ||||||||||||||||||||||||||||||||||||||||||||
\WeDevs\Dokan\Models\VendorBalance::class, | ||||||||||||||||||||||||||||||||||||||||||||
\WeDevs\Dokan\Models\DataStore\VendorBalanceStore::class, | ||||||||||||||||||||||||||||||||||||||||||||
]; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||
* {@inheritDoc} | ||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||
* Check if the service provider can provide the given service alias. | ||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||
* @param string $alias The service alias to check. | ||||||||||||||||||||||||||||||||||||||||||||
* @return bool True if the service provider can provide the service, false otherwise. | ||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||
public function provides( string $alias ): bool { | ||||||||||||||||||||||||||||||||||||||||||||
return in_array( $alias, $this->services, true ); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||
* Register the classes. | ||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||
public function register(): void { | ||||||||||||||||||||||||||||||||||||||||||||
foreach ( $this->services as $service ) { | ||||||||||||||||||||||||||||||||||||||||||||
$this->getContainer()->add( $service, $service ); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+33
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider adding error handling and service validation. The
/**
* Register the classes.
+ *
+ * @throws \Exception If a service class does not exist
*/
public function register(): void {
foreach ( $this->services as $service ) {
+ if (!class_exists($service)) {
+ throw new \Exception("Service class {$service} does not exist");
+ }
+ if ($this->getContainer()->has($service)) {
+ continue;
+ }
$this->getContainer()->add( $service, $service );
}
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,104 @@ | ||||||||||||||||||||||||||||||||||||||||||
<?php | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
namespace WeDevs\Dokan\Models; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
use WC_Data; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
abstract class BaseModel extends WC_Data { | ||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Save should create or update based on object existence. | ||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||
* @return int | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
public function save() { | ||||||||||||||||||||||||||||||||||||||||||
// wc_get_product() | ||||||||||||||||||||||||||||||||||||||||||
if ( ! $this->data_store ) { | ||||||||||||||||||||||||||||||||||||||||||
return $this->get_id(); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Trigger action before saving to the DB. Allows you to adjust object props before save. | ||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||
* @param WC_Data $this The object being saved. | ||||||||||||||||||||||||||||||||||||||||||
* @param WC_Data_Store_WP $data_store THe data store persisting the data. | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct capitalization in parameter descriptions. In the docblocks for Apply this diff to fix the typos: - * @param WC_Data_Store_WP $data_store THe data store persisting the data.
+ * @param WC_Data_Store_WP $data_store The data store persisting the data. Also applies to: 37-37 |
||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
do_action( 'dokan_before_' . $this->object_type . '_object_save', $this, $this->data_store ); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if ( $this->get_id() ) { | ||||||||||||||||||||||||||||||||||||||||||
$this->data_store->update( $this ); | ||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||
$this->data_store->create( $this ); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Trigger action after saving to the DB. | ||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||
* @param WC_Data $this The object being saved. | ||||||||||||||||||||||||||||||||||||||||||
* @param WC_Data_Store_WP $data_store THe data store persisting the data. | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
do_action( 'dokan_after_' . $this->object_type . '_object_save', $this, $this->data_store ); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
return $this->get_id(); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Delete an object, set the ID to 0, and return result. | ||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||
* @param bool $force_delete Should the date be deleted permanently. | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix typo in parameter description of In the docblock for the Apply this diff to correct the typo: - * @param bool $force_delete Should the date be deleted permanently.
+ * @param bool $force_delete Should the data be deleted permanently. 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
* @return bool result | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
public function delete( $force_delete = false ) { | ||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Filters whether an object deletion should take place. Equivalent to `pre_delete_post`. | ||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||
* @param mixed $check Whether to go ahead with deletion. | ||||||||||||||||||||||||||||||||||||||||||
* @param Data $this The data object being deleted. | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure consistency in parameter types in docblocks. At line 55, the parameter type for Apply this diff to correct the parameter type: - * @param Data $this The data object being deleted.
+ * @param WC_Data $this The data object being deleted. 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
* @param bool $force_delete Whether to bypass the trash. | ||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||
* @since 8.1.0. | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
$check = apply_filters( "dokan_pre_delete_$this->object_type", null, $this, $force_delete ); | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix variable interpolation in When interpolating object properties within double-quoted strings, you need to enclose the property in braces to ensure correct parsing. Without braces, PHP may not parse the variable correctly. Apply this diff to fix the variable interpolation: - $check = apply_filters( "dokan_pre_delete_$this->object_type", null, $this, $force_delete );
+ $check = apply_filters( "dokan_pre_delete_{$this->object_type}", null, $this, $force_delete ); 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if ( null !== $check ) { | ||||||||||||||||||||||||||||||||||||||||||
return $check; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if ( $this->data_store ) { | ||||||||||||||||||||||||||||||||||||||||||
$this->data_store->delete( $this, array( 'force_delete' => $force_delete ) ); | ||||||||||||||||||||||||||||||||||||||||||
$this->set_id( 0 ); | ||||||||||||||||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
return false; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Delete raws from the database. | ||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||
* @param array $data Array of args to delete an object, e.g. `array( 'id' => 1, status => ['draft', 'cancelled'] )` or `array( 'id' => 1, 'status' => 'publish' )`. | ||||||||||||||||||||||||||||||||||||||||||
* @return bool result | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
public static function delete_by( array $data ) { | ||||||||||||||||||||||||||||||||||||||||||
$object = new static(); | ||||||||||||||||||||||||||||||||||||||||||
return $object->data_store->delete_by( $data ); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+81
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance delete_by method with better type safety and error handling. The static delete_by method needs:
+ /**
+ * Delete rows from the database.
+ *
+ * @param array $data Array of args to delete objects.
+ * @return bool True on success, false on failure.
+ * @throws \Exception When data store operation fails.
+ */
- public static function delete_by( array $data ) {
+ public static function delete_by( array $data ): bool {
$object = new static();
+ try {
return $object->data_store->delete_by( $data );
+ } catch ( \Exception $e ) {
+ wc_doing_it_wrong( __FUNCTION__, $e->getMessage(), '3.0.0' );
+ return false;
+ }
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Prefix for action and filter hooks on data. | ||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||
* @return string | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
protected function get_hook_prefix() { | ||||||||||||||||||||||||||||||||||||||||||
return 'dokan_' . $this->object_type . '_get_'; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||
* Get All Meta Data. | ||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||
* @since 2.6.0 | ||||||||||||||||||||||||||||||||||||||||||
* @return array of objects. | ||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||
public function get_meta_data() { | ||||||||||||||||||||||||||||||||||||||||||
return apply_filters( $this->get_hook_prefix() . 'meta_data', array() ); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+101
to
+103
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Review meta data implementation. The current implementation seems incomplete:
Consider implementing proper meta data retrieval: public function get_meta_data() {
- return apply_filters( $this->get_hook_prefix() . 'meta_data', array() );
+ $meta_data = parent::get_meta_data();
+ return apply_filters( $this->get_hook_prefix() . 'meta_data', $meta_data );
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
} |
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.
Fix implementation issues in DepartmentStore class.
The DepartmentStore implementation has the following issues:
get_date_updated
method appears to be incorrectly placed in the store class instead of the model classApply these changes: