Skip to content
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

Open
wants to merge 27 commits into
base: develop
Choose a base branch
from
Open

feat: Dokan Data Store #2346

wants to merge 27 commits into from

Conversation

shohag121
Copy link
Member

@shohag121 shohag121 commented Aug 21, 2024

All Submissions:

  • My code follow the WordPress' coding standards
  • My code satisfies feature requirements
  • My code is tested
  • My code passes the PHPCS tests
  • My code has proper inline documentation
  • I've included related pull request(s) (optional)
  • I've included developer documentation (optional)
  • I've added proper labels to this pull request

Changes proposed in this Pull Request:

Related Pull Request(s)

  • Full PR Link

Closes

How to test the changes in this Pull Request:

  • Steps or issue link

Changelog entry

Title

Detailed Description of the pull request. What was previous behaviour
and what will be changed in this PR.

Before Changes

Describe the issue before changes with screenshots(s).

After Changes

Describe the issue after changes with screenshot(s).

Feature Video (optional)

Link of detailed video if this PR is for a feature.

PR Self Review Checklist:

  • Code is not following code style guidelines
  • Bad naming: make sure you would understand your code if you read it a few months from now.
  • KISS: Keep it simple, Sweetie (not stupid!).
  • DRY: Don't Repeat Yourself.
  • Code that is not readable: too many nested 'if's are a bad sign.
  • Performance issues
  • Complicated constructions that need refactoring or comments: code should almost always be self-explanatory.
  • Grammar errors.

FOR PR REVIEWER ONLY:

As a reviewer, your feedback should be focused on the idea, not the person. Seek to understand, be respectful, and focus on constructive dialog.

As a contributor, your responsibility is to learn from suggestions and iterate your pull request should it be needed based on feedback. Seek to collaborate and produce the best possible contribution to the greater whole.

  • Correct — Does the change do what it’s supposed to? ie: code 100% fulfilling the requirements?
  • Secure — Would a nefarious party find some way to exploit this change? ie: everything is sanitized/escaped appropriately for any SQL or XSS injection possibilities?
  • Readable — Will your future self be able to understand this change months down the road?
  • Elegant — Does the change fit aesthetically within the overall style and architecture?

Summary by CodeRabbit

  • New Features

    • Introduced a BaseModel class for managing data persistence.
    • Added a VendorBalance class to handle vendor balance data with various properties and transaction types.
    • Implemented a VendorBalanceStore class for structured management of vendor balance records.
    • Added a new DataStoreInterface for defining data management operations.
    • Enhanced the Commission and Order classes to utilize the new VendorBalance model for updating vendor balances.
    • Provided a comprehensive guide for developers on utilizing the Dokan Model and Data Store effectively.
    • Introduced a new method for asserting database absence in the test suite.
  • Bug Fixes

    • Established comprehensive unit tests for the VendorBalance model to ensure correct functionality of save, update, delete, and read operations.
    • Corrected typos in method names for consistency and clarity.
  • Documentation

    • Enhanced documentation for methods in the VendorBalance class, detailing parameter and return types.
    • Updated documentation for the Dokan Model and Data Store, including examples and implementation details.

@shohag121 shohag121 added the DO NOT MERGE Don't merge this PR label Aug 21, 2024
@shohag121 shohag121 self-assigned this Aug 21, 2024
Copy link
Contributor

coderabbitai bot commented Aug 21, 2024

Walkthrough

The changes introduce several new classes and interfaces within the WeDevs\Dokan\Models namespace, establishing a structured framework for managing vendor balance data. Key additions include the BaseModel abstract class, the DataStoreInterface, and the BaseDataStore class, which facilitate data persistence operations. The VendorBalanceStore and VendorBalance classes extend these foundations to handle vendor-specific data. Additionally, a test suite for the VendorBalance model is created to ensure proper functionality of save, update, and delete operations.

Changes

File Path Change Summary
includes/Models/BaseModel.php Added abstract class BaseModel with methods save(), delete(), delete_by(), get_hook_prefix(), and get_meta_data().
includes/Models/DataStore/DataStoreInterface.php Added interface DataStoreInterface with methods create(), read(), update(), and delete().
includes/Models/DataStore/BaseDataStore.php Added abstract class BaseDataStore implementing DataStoreInterface with methods for CRUD operations and utility methods.
includes/Models/DataStore/VendorBalanceStore.php Added class VendorBalanceStore extending BaseDataStore with methods for managing vendor balance data.
includes/Models/VendorBalance.php Added class VendorBalance extending BaseModel with properties for vendor balance data and associated methods.
tests/php/src/Models/VendorBalanceModelTest.php Added test class VendorBalanceModelTest with methods to test save, update, delete, and read functionality of VendorBalance.
includes/Commission.php Updated calculate_gateway_fee method to use VendorBalance::update_by_transaction for updating vendor balances; corrected method names for quantity.
includes/Order/Hooks.php Refactored on_order_status_change method to use VendorBalance::update_by_transaction for updating vendor balances; added check for exclude_cod_payment.
includes/Order/functions.php Replaced direct database insertions with VendorBalance object instantiation in dokan_sync_insert_order; marked several functions as deprecated.
includes/Vendor/Vendor.php Updated get_earnings method to use VendorBalance::get_total_earning_by_vendor for calculating vendor earnings.
tests/php/src/CustomAssertion/DBAssertionTrait.php Added assertDatabaseMissing method to assert that a specified database table does not contain any rows matching the provided criteria.

Possibly related PRs

  • Add $data parameter to dokan_update_vendor hook #2386: The changes in the main PR involve the introduction of the BaseModel class and its methods for data persistence, which are foundational for the DataStoreInterface and related classes like VendorBalanceStore.
  • Add & Refactor vendor payment tests #2387: The enhancements to vendor payment tests in this PR are relevant as they likely interact with the VendorBalance class introduced in the main PR, which manages vendor financial data.
  • enhance: Hide revenue coupon info from woocommerce analytics report. #2418: The modifications to the WooCommerce analytics report to hide revenue coupon information may interact with the vendor balance functionalities introduced in the main PR, as both deal with financial data management within the Dokan framework.
  • Refactor/vendor balance raw query #2430: The refactor of the vendor balance raw query aligns with the introduction of the BaseModel and VendorBalance classes in the main PR, as both aim to improve the management and retrieval of vendor-related data within the Dokan system.

Suggested labels

Ready to Merge, :+1: Dev Review Done, Upcoming Release

Suggested reviewers

  • shohag121

🐰 In the land of code, where models play,
A BaseModel was born today.
With VendorBalance to keep track,
Our data's safe, there's no lack!
So hop along, let's test with glee,
For every save and update, we’ll see! 🐇


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Outside diff range, codebase verification and nitpick comments (2)
includes/DataStore/TestModel.php (1)

9-9: Clarify the object type.

The comment suggests changing the object type. Ensure it reflects the intended use.

includes/DataStore/DataStore.php (1)

23-50: Clarify object type handling in the constructor.

The logic for handling object types could be clarified by adding comments or restructuring the code for better readability.

Consider adding comments or restructuring the code to make the object type handling clearer.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 0d2b6aa and ea2a6e9.

Files selected for processing (6)
  • includes/DataStore/AbstractDataStore.php (1 hunks)
  • includes/DataStore/DataStore.php (1 hunks)
  • includes/DataStore/DataStoreInterface.php (1 hunks)
  • includes/DataStore/Model.php (1 hunks)
  • includes/DataStore/TestDataStore.php (1 hunks)
  • includes/DataStore/TestModel.php (1 hunks)
Additional comments not posted (7)
includes/DataStore/AbstractDataStore.php (1)

1-7: LGTM! Abstract class setup is appropriate.

The AbstractDataStore class correctly extends SqlQuery, providing a foundation for further data store implementations.

includes/DataStore/Model.php (1)

19-28: Verify data store loading in the constructor.

The constructor attempts to load a data store and handle exceptions. Ensure that DataStore::load and get_instance are correctly implemented.

Run the following script to verify the implementation of DataStore::load and get_instance:

Verification successful

Data store loading in the constructor is verified successfully.

The DataStore::load and get_instance methods are correctly implemented, ensuring the data store is properly loaded in the constructor. No issues found.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of DataStore::load and get_instance.

# Test: Search for the DataStore class and its methods.
rg --type php -A 5 'class DataStore'

Length of output: 366


Script:

#!/bin/bash
# Description: Verify the implementation of DataStore::load and get_instance.

# Test: Search for the load method in the DataStore class.
rg --type php -A 10 'function load' includes/DataStore/DataStore.php

# Test: Search for the get_instance method in the DataStore class.
rg --type php -A 10 'function get_instance' includes/DataStore/DataStore.php

Length of output: 514

includes/DataStore/DataStoreInterface.php (1)

1-80: LGTM! Interface definition is appropriate.

The DataStoreInterface provides a clear contract for data store implementations, covering CRUD operations and querying.

includes/DataStore/DataStore.php (4)

3-5: Namespace and imports are correctly defined.

The namespace WeDevs\Dokan\DataStore is appropriate, and the Exception import is necessary for error handling.


7-18: Class declaration and properties are well-defined.

The DataStore class and its protected properties are appropriately declared, promoting encapsulation.


56-58: Static load method is correctly implemented.

The method correctly creates and returns a new DataStore instance.


63-65: get_instance method is correctly implemented.

The method correctly returns the data store instance.

Comment on lines 14 to 16
public function get(): Model {
// TODO: Implement get() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement the get method.

The get method is currently unimplemented. Consider implementing it to retrieve model data.

Would you like assistance in implementing this method?

Comment on lines 21 to 23
public function create(): Model {
// TODO: Implement create() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement the create method.

The create method is currently unimplemented. Consider implementing it to create new model data.

Would you like assistance in implementing this method?

Comment on lines 28 to 30
public function update(): Model {
// TODO: Implement update() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement the update method.

The update method is currently unimplemented. Consider implementing it to update existing model data.

Would you like assistance in implementing this method?

Comment on lines 35 to 37
public function delete(): bool {
// TODO: Implement delete() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement the delete method.

The delete method is currently unimplemented. Consider implementing it to delete model data.

Would you like assistance in implementing this method?

Comment on lines 13 to 15
public function create( &$model ) {
// TODO: Implement create() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement the create method.

The create method is currently unimplemented. Consider implementing it to handle model creation.

Would you like assistance in implementing this method?

Comment on lines 27 to 29
public function delete( &$model ) {
// TODO: Implement delete() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement the delete method.

The delete method is currently unimplemented. Consider implementing it to handle model deletion.

Would you like assistance in implementing this method?

Comment on lines 34 to 36
public function get( &$model ) {
// TODO: Implement get() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement the get method.

The get method is currently unimplemented. Consider implementing it to retrieve model data.

Would you like assistance in implementing this method?

Comment on lines 41 to 43
public function query( array $args = [] ): array {
// TODO: Implement query() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement the query method.

The query method is currently unimplemented. Consider implementing it to handle data queries.

Would you like assistance in implementing this method?

Comment on lines 48 to 50
public function count( array $args = [] ): int {
// TODO: Implement count() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement the count method.

The count method is currently unimplemented. Consider implementing it to count data entries.

Would you like assistance in implementing this method?

Comment on lines 39 to 66
abstract public function get(): Model;

/**
* Store a new model data.
*
* @since DOKAN_SINCE
*
* @return Model
*/
abstract public function create(): Model;

/**
* Update the model data.
*
* @since DOKAN_SINCE
*
* @return Model
*/
abstract public function update(): Model;

/**
* Delete the model data.
*
* @since DOKAN_SINCE
*
* @return bool
*/
abstract public function delete(): bool;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure implementation of abstract methods.

The abstract methods get, create, update, and delete need to be implemented in derived classes.

Would you like assistance in implementing these methods in a derived class?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 26

🧹 Outside diff range and nitpick comments (13)
tests/php/src/DataStore/StoreTest.php (2)

7-13: LGTM: Class declaration and property are well-defined.

The class name follows the convention for test classes, and the property is correctly defined with appropriate visibility and documentation.

Consider adding a class-level PHPDoc block to provide a brief description of the StoreTest class and its purpose.


1-18: Overall: Good structure, but needs more comprehensive testing.

The file provides a good starting point for Store testing, with correct namespace, class structure, and basic setup. However, to ensure robust testing of the Store functionality:

  1. Implement multiple test methods covering various aspects of the Store.
  2. Add setup and teardown methods if needed for test data management.
  3. Include edge cases and error scenarios in your tests.
  4. Add a class-level PHPDoc block describing the purpose of StoreTest.

These improvements will enhance the test coverage and maintainability of the Store feature.

Consider organizing tests into separate methods for different Store operations (e.g., test_store_create(), test_store_read(), etc.) to improve test clarity and coverage.

includes/Models/DataStore/TestModel.php (2)

1-7: Add a file-level docblock for better documentation.

Consider adding a file-level docblock at the beginning of the file to provide a brief description of the purpose of this class and any relevant information for developers who might work with it in the future.

Here's a suggested docblock:

/**
 * TestModel class file.
 *
 * This file contains the TestModel class which extends the base Model class
 * and provides specific implementation for the 'test' object type.
 *
 * @package WeDevs\Dokan\DataStore
 */

1-38: Add PHPDoc comments for all methods.

To improve code documentation and maintainability, consider adding PHPDoc comments for each method. This will provide better context for developers working with this class in the future.

Here's an example for the get() method:

/**
 * Get the test object.
 *
 * @since [version]
 *
 * @throws \WeDevs\Dokan\Exceptions\DokanException When the object cannot be found.
 *
 * @return Model The current object instance.
 */
public function get(): Model {
    // Implementation here
}

Apply similar PHPDoc comments to all other methods in the class.

includes/Models/DataStore/TestDataStore.php (1)

1-8: Enhance class and method documentation.

While the use of @inheritDoc tags is good for inheriting interface documentation, additional context would improve code clarity:

  1. Add a class-level PHPDoc block describing the purpose, usage, and any important details about TestDataStore.
  2. Consider adding method-level documentation to provide context on the expected behavior, even if it inherits from the interface. This is particularly important for methods like query and count, where implementation details might vary.

Here's an example of improved documentation for the class and the query method:

/**
 * TestDataStore class for managing test-related data operations.
 *
 * This class provides an implementation of the DataStoreInterface
 * specifically tailored for test data management within the Dokan plugin.
 *
 * @since x.x.x
 */
class TestDataStore extends AbstractDataStore implements DataStoreInterface {
    // ...

    /**
     * @inheritDoc
     *
     * Queries test data based on the provided arguments.
     *
     * @param array $args An array of query arguments. Possible keys include:
     *                    - 'limit': int The maximum number of results to return.
     *                    - 'offset': int The number of results to skip.
     *                    // Add other relevant arguments
     *
     * @return array An array of test data matching the query arguments.
     */
    public function query( array $args = [] ): array {
        // TODO: Implement query() method.
    }

    // ...
}

Also applies to: 10-13, 17-20, 24-27, 31-34, 38-41, 45-48

includes/Models/DataStore/DataStoreInterface.php (3)

5-10: Replace DOKAN_SINCE placeholder with actual version number.

The @since tag in the interface's PHPDoc block is using a placeholder DOKAN_SINCE. This should be replaced with the actual version number in which this interface is being introduced.


23-33: Consider specifying the exception type.

The PHPDoc for the update method mentions that it may throw an exception. It would be more informative to specify the type of exception that might be thrown, e.g., @throws \InvalidArgumentException.


35-45: Consider specifying the exception type.

Similar to the update method, the delete method's PHPDoc mentions that it may throw an exception. It would be beneficial to specify the type of exception that might be thrown, e.g., @throws \RuntimeException.

includes/Models/DataStore/Model.php (2)

12-13: Replace DOKAN_SINCE with the appropriate version number

The @since DOKAN_SINCE tags should be replaced with the actual version number of the plugin when the feature is introduced.

Ensure to update all occurrences before releasing to maintain accurate documentation.

Also applies to: 35-36, 44-45, 53-54, 62-63


16-16: Organize property declarations for better readability

Group protected properties together at the beginning of the class for improved maintainability and readability.

Reorder the properties:

 protected int $id;
 protected string $object_type;
+protected DataStoreInterface $data_store;

Also applies to: 30-30

includes/Models/DataStore/DataStore.php (2)

12-14: Remove redundant docblocks for typed properties

Properties $instance and $current_class_name are already type-hinted with DataStoreInterface and string respectively. The @var annotations are redundant.

Consider removing the docblocks to keep the code clean:

12-/**
13- * @var DataStoreInterface
14- */
15 protected DataStoreInterface $instance;

16-/**
17- * @var string
18- */
19 protected string $current_class_name;

Also applies to: 16-18


38-38: Provide more descriptive exception messages

The exception messages 'Invalid data store.' lack context, which might make debugging difficult.

Consider including the $object_type or $store in the exception messages for better clarity:

38 throw new Exception( esc_html__( 'Invalid data store: ' . get_class( $store ), 'dokan-lite' ) );
44 throw new Exception( esc_html__( 'Data store class not found: ' . $store, 'dokan-lite' ) );
50 throw new Exception( esc_html__( 'Data store not defined for object type: ' . $object_type, 'dokan-lite' ) );

This provides more informative error messages that can aid in troubleshooting.

Also applies to: 44-44, 50-50

includes/Models/BaseModel.php (1)

14-14: Remove unnecessary comment in save() method.

The comment // wc_get_product() on line 14 appears to be unrelated to the save() method and may cause confusion. Consider removing it to keep the code clean and maintainable.

Apply this diff to remove the unnecessary comment:

-		// wc_get_product()
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between ea2a6e9 and 1628844.

📒 Files selected for processing (8)
  • includes/Models/BaseModel.php (1 hunks)
  • includes/Models/DataStore/AbstractDataStore.php (1 hunks)
  • includes/Models/DataStore/DataStore.php (1 hunks)
  • includes/Models/DataStore/DataStoreInterface.php (1 hunks)
  • includes/Models/DataStore/Model.php (1 hunks)
  • includes/Models/DataStore/TestDataStore.php (1 hunks)
  • includes/Models/DataStore/TestModel.php (1 hunks)
  • tests/php/src/DataStore/StoreTest.php (1 hunks)
🧰 Additional context used
🪛 GitHub Check: Run PHPCS inspection
includes/Models/DataStore/AbstractDataStore.php

[failure] 85-85:
Use placeholders and $wpdb->prepare(); found interpolated variable {$table_name} at "SELECT * FROM {$table_name} WHERE {$id_field_name} = $format"


[failure] 85-85:
Use placeholders and $wpdb->prepare(); found interpolated variable {$id_field_name} at "SELECT * FROM {$table_name} WHERE {$id_field_name} = $format"


[failure] 85-85:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at "SELECT * FROM {$table_name} WHERE {$id_field_name} = $format"


[warning] 85-85:
Replacement variables found, but no valid placeholders found in the query.


[failure] 130-130:
Use placeholders and $wpdb->prepare(); found interpolated variable {$table_name} at "DELETE FROM {$table_name}


[failure] 131-131:
Use placeholders and $wpdb->prepare(); found interpolated variable $id_field_name at WHERE $id_field_name = $format"


[failure] 131-131:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at WHERE $id_field_name = $format"


[warning] 131-131:
Replacement variables found, but no valid placeholders found in the query.

includes/Models/DataStore/DataStore.php

[failure] 11-11:
Whitespace found at end of line

🔇 Additional comments (6)
tests/php/src/DataStore/StoreTest.php (1)

1-5: LGTM: Namespace and use statement are correct.

The namespace follows the PSR-4 autoloading standard, and the use statement correctly imports the base test case class.

includes/Models/DataStore/TestDataStore.php (1)

1-8: LGTM: Class structure and namespace are well-defined.

The TestDataStore class is correctly placed in the WeDevs\Dokan\Models\DataStore namespace, extends AbstractDataStore, and implements DataStoreInterface. The use statements are properly included.

includes/Models/DataStore/DataStoreInterface.php (3)

12-21: LGTM: create method is well-defined.

The create method is correctly declared with appropriate parameter and return type. The use of a reference for the Model parameter is a good choice for efficiency.


59-68: LGTM: query method is well-defined.

The query method is correctly declared with an appropriate parameter and return type. The optional array parameter provides flexibility for various query scenarios.


70-80: LGTM: count method is well-defined.

The count method is correctly declared with an appropriate parameter and return type. The optional array parameter provides flexibility for various counting scenarios.

includes/Models/DataStore/Model.php (1)

22-24: Verify the existence of methods used in $store->get_instance()

Ensure that the DataStore class has the get_instance() method, and the returned instance has a get() method that accepts the model.

Run the following script to confirm the presence of these methods:

Comment on lines 15 to 17
public function test_store() {
$this->assertTrue( true );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enhance the test method with meaningful assertions.

The current test method is a placeholder and doesn't test any actual functionality of the Store. Consider the following improvements:

  1. Implement actual test cases for the Store functionality.
  2. Add setup and teardown methods if needed for Store testing.
  3. Use descriptive test method names that indicate what is being tested.

Would you like assistance in generating more comprehensive test cases for the Store functionality?

Comment on lines 11 to 16
/**
* @inheritDoc
*/
public function get(): Model {
// TODO: Implement get() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement the get() method.

The get() method is currently unimplemented. This method should retrieve and return a Model instance based on some identifier.

Consider implementing it as follows:

public function get(): Model {
    // Retrieve the data from the data store
    $data = $this->data_store->read($this->get_id());
    
    // Update the current object with the retrieved data
    $this->set_props($data);
    
    return $this;
}

Ensure to handle cases where the data might not be found, possibly by throwing a \WeDevs\Dokan\Exceptions\DokanException.

Comment on lines 18 to 23
/**
* @inheritDoc
*/
public function create(): Model {
// TODO: Implement create() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement the create() method.

The create() method is currently unimplemented. This method should create a new entry in the data store and return the updated Model instance.

Consider implementing it as follows:

public function create(): Model {
    if ($this->get_id()) {
        throw new \WeDevs\Dokan\Exceptions\DokanException('dokan_invalid_operation', __('Cannot create an existing test object.', 'dokan-lite'));
    }

    // Prepare data for creation
    $data = $this->get_changes();

    // Create the entry in the data store
    $id = $this->data_store->create($data);

    // Set the ID of the newly created object
    $this->set_id($id);

    // Clear the changes as they've been saved
    $this->clear_changes();

    return $this;
}

Ensure to handle any potential errors during the creation process.

Comment on lines 25 to 30
/**
* @inheritDoc
*/
public function update(): Model {
// TODO: Implement update() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement the update() method.

The update() method is currently unimplemented. This method should update an existing entry in the data store and return the updated Model instance.

Consider implementing it as follows:

public function update(): Model {
    if (!$this->get_id()) {
        throw new \WeDevs\Dokan\Exceptions\DokanException('dokan_invalid_operation', __('Cannot update a test object without an ID.', 'dokan-lite'));
    }

    // Get only the changed data
    $changes = $this->get_changes();

    if (empty($changes)) {
        return $this;
    }

    // Update the entry in the data store
    $this->data_store->update($this->get_id(), $changes);

    // Clear the changes as they've been saved
    $this->clear_changes();

    return $this;
}

Ensure to handle any potential errors during the update process.

Comment on lines 32 to 37
/**
* @inheritDoc
*/
public function delete(): bool {
// TODO: Implement delete() method.
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement the delete() method.

The delete() method is currently unimplemented. This method should delete the entry from the data store and return a boolean indicating success or failure.

Consider implementing it as follows:

public function delete(): bool {
    if (!$this->get_id()) {
        throw new \WeDevs\Dokan\Exceptions\DokanException('dokan_invalid_operation', __('Cannot delete a test object without an ID.', 'dokan-lite'));
    }

    // Delete the entry from the data store
    $result = $this->data_store->delete($this->get_id());

    if ($result) {
        $this->set_id(0);
        $this->clear_changes();
    }

    return $result;
}

Ensure to handle any potential errors during the deletion process.

* 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure consistency in parameter types in docblocks.

At line 55, the parameter type for $this is listed as Data. To maintain consistency and accuracy, it should be WC_Data, matching the type used in other methods.

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* @param Data $this The data object being deleted.
* @param WC_Data $this The data object being deleted.

*
* @since 8.1.0.
*/
$check = apply_filters( "dokan_pre_delete_$this->object_type", null, $this, $force_delete );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix variable interpolation in apply_filters call.

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$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 );

Comment on lines 192 to 194
if ( ! str_starts_with( $table_name, $wpdb->prefix ) ) {
$table_name = $wpdb->prefix . $table_name;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use Compatible Functions to Maintain PHP Version Support

The function str_starts_with() is used, which is available only from PHP 8.0 onwards. If the codebase needs to support earlier PHP versions (e.g., PHP 7.x), consider using an alternative function to ensure compatibility.

You can replace str_starts_with() with a compatible approach:

-        if ( ! str_starts_with( $table_name, $wpdb->prefix ) ) {
+        if ( strpos( $table_name, $wpdb->prefix ) !== 0 ) {

This change ensures that the code remains compatible with PHP versions prior to 8.0.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ( ! str_starts_with( $table_name, $wpdb->prefix ) ) {
$table_name = $wpdb->prefix . $table_name;
}
if ( strpos( $table_name, $wpdb->prefix ) !== 0 ) {
$table_name = $wpdb->prefix . $table_name;
}

Comment on lines 85 to 88
"SELECT * FROM {$table_name} WHERE {$id_field_name} = $format",
$model->get_id()
)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid Direct Variable Interpolation in SQL Queries; Use Safe Practices to Prevent SQL Injection

Interpolating table and column names directly into SQL queries can lead to SQL injection vulnerabilities. Even when using $wpdb->prepare(), variables interpolated into the query are not sanitized. Table names and column names should be hard-coded or properly sanitized before inclusion in the query.

Consider refactoring the query to avoid variable interpolation:

-               "SELECT * FROM {$table_name} WHERE {$id_field_name} = $format",
+               "SELECT * FROM " . esc_sql( $table_name ) . " WHERE " . esc_sql( $id_field_name ) . " = $format",

Ensure that $table_name and $id_field_name are sanitized using esc_sql() to prevent SQL injection vulnerabilities.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"SELECT * FROM {$table_name} WHERE {$id_field_name} = $format",
$model->get_id()
)
);
"SELECT * FROM " . esc_sql( $table_name ) . " WHERE " . esc_sql( $id_field_name ) . " = %s",
$model->get_id()
)
);
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 85-85:
Use placeholders and $wpdb->prepare(); found interpolated variable {$table_name} at "SELECT * FROM {$table_name} WHERE {$id_field_name} = $format"


[failure] 85-85:
Use placeholders and $wpdb->prepare(); found interpolated variable {$id_field_name} at "SELECT * FROM {$table_name} WHERE {$id_field_name} = $format"


[failure] 85-85:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at "SELECT * FROM {$table_name} WHERE {$id_field_name} = $format"


[warning] 85-85:
Replacement variables found, but no valid placeholders found in the query.

Comment on lines 130 to 134
"DELETE FROM {$table_name}
WHERE $id_field_name = $format",
$id
)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid Direct Variable Interpolation in SQL Queries; Use Safe Practices to Prevent SQL Injection

Interpolating table and column names directly into SQL queries can lead to SQL injection vulnerabilities. Even when using $wpdb->prepare(), variables interpolated into the query are not sanitized. Table names and column names should be hard-coded or properly sanitized before inclusion in the query.

Consider refactoring the query to avoid variable interpolation:

-               "DELETE FROM {$table_name}
-               WHERE $id_field_name = $format",
+               "DELETE FROM " . esc_sql( $table_name ) . " WHERE " . esc_sql( $id_field_name ) . " = $format",

Ensure that $table_name and $id_field_name are sanitized using esc_sql() to prevent SQL injection vulnerabilities.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"DELETE FROM {$table_name}
WHERE $id_field_name = $format",
$id
)
);
"DELETE FROM " . esc_sql( $table_name ) . " WHERE " . esc_sql( $id_field_name ) . " = $format",
$id
)
);
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 130-130:
Use placeholders and $wpdb->prepare(); found interpolated variable {$table_name} at "DELETE FROM {$table_name}


[failure] 131-131:
Use placeholders and $wpdb->prepare(); found interpolated variable $id_field_name at WHERE $id_field_name = $format"


[failure] 131-131:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at WHERE $id_field_name = $format"


[warning] 131-131:
Replacement variables found, but no valid placeholders found in the query.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Outside diff range and nitpick comments (6)
includes/Models/DataStore/VendorBalanceStore.php (1)

22-24: LGTM: get_table_name() method is well-defined.

The method is correctly implemented with proper visibility and return type hint.

Consider using a class constant for the table name to make it easier to update in the future:

+ private const TABLE_NAME = 'dokan_vendor_balance';

  public function get_table_name(): string {
-     return 'dokan_vendor_balance';
+     return self::TABLE_NAME;
  }
includes/Models/DataStore/DataStoreInterface.php (3)

5-10: Replace the version placeholder with the actual version number.

The PHPDoc block contains a placeholder DOKAN_SINCE for the version number. Please replace this with the actual version number in which this interface is being introduced.


23-33: Specify the exception type in the PHPDoc.

The PHPDoc mentions that this method may throw an exception, but it doesn't specify the type. Consider adding the specific exception type(s) that may be thrown, for example:

* @throws \RuntimeException If the update operation fails.

This will provide more clarity for developers implementing this interface.


35-45: Specify the exception type in the PHPDoc.

Similar to the update method, the PHPDoc for the delete method mentions that it may throw an exception without specifying the type. Consider adding the specific exception type(s) that may be thrown, for example:

* @throws \RuntimeException If the deletion operation fails.

This will provide more clarity for developers implementing this interface.

includes/Models/VendorBalance.php (1)

7-15: Convert property comments to PHP DocBlock format.

The commented-out property definitions provide useful information about the data structure. Consider converting these into a proper PHP DocBlock format for better code documentation and IDE support.

Here's an example of how you could format this:

/**
 * @property int    $vendor_id
 * @property int    $trn_id
 * @property string $trn_type
 * @property string $perticulars
 * @property float  $debit
 * @property float  $credit
 * @property string $status
 * @property string $trn_date
 * @property string $balance_date
 */
class VendorBalance extends BaseModel {
    // ... rest of the class
}
includes/Models/DataStore/BaseDataStore.php (1)

205-207: Verify the hook prefix generation for consistency

The get_hook_prefix() method constructs the hook prefix by stripping the $wpdb->prefix from the $table_name. If $table_name does not contain $wpdb->prefix, this might lead to an incorrect hook prefix.

Consider adjusting the hook prefix generation to ensure it accurately represents the table name:

-        $hook_prefix = str_replace( $wpdb->prefix, '', $table_name );
+        $table_name_without_prefix = preg_replace( '/^' . preg_quote( $wpdb->prefix, '/' ) . '/', '', $table_name );
+        $hook_prefix = $table_name_without_prefix;

Ensure that $table_name actually starts with $wpdb->prefix before attempting to remove it.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 1628844 and 40e5ecb.

📒 Files selected for processing (5)
  • includes/Models/BaseModel.php (1 hunks)
  • includes/Models/DataStore/BaseDataStore.php (1 hunks)
  • includes/Models/DataStore/DataStoreInterface.php (1 hunks)
  • includes/Models/DataStore/VendorBalanceStore.php (1 hunks)
  • includes/Models/VendorBalance.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • includes/Models/BaseModel.php
🧰 Additional context used
🪛 GitHub Check: Run PHPCS inspection
includes/Models/DataStore/BaseDataStore.php

[failure] 63-63:
Use placeholders and $wpdb->prepare(); found interpolated variable {$id_field_name} at "{$id_field_name} = $format"


[failure] 63-63:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at "{$id_field_name} = $format"


[warning] 63-63:
Replacement variables found, but no valid placeholders found in the query.


[warning] 69-69:
Use placeholders and $wpdb->prepare(); found $this


[warning] 69-69:
Use placeholders and $wpdb->prepare(); found get_query_statement


[failure] 140-140:
Use placeholders and $wpdb->prepare(); found interpolated variable {$table_name} at "DELETE FROM {$table_name}


[failure] 141-141:
Use placeholders and $wpdb->prepare(); found interpolated variable $id_field_name at WHERE $id_field_name = $format"


[failure] 141-141:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at WHERE $id_field_name = $format"


[warning] 141-141:
Replacement variables found, but no valid placeholders found in the query.

🔇 Additional comments (6)
includes/Models/DataStore/VendorBalanceStore.php (1)

1-5: LGTM: Proper namespace and class structure.

The namespace and class declaration follow best practices:

  • Namespace adheres to PSR-4 autoloading standard.
  • Class name matches the filename.
  • Proper use of inheritance by extending BaseDataStore.
includes/Models/VendorBalance.php (5)

1-5: LGTM: Class structure and namespace are well-defined.

The VendorBalance class is correctly placed in the WeDevs\Dokan\Models namespace and extends BaseModel, which is a good practice for maintaining consistency across model classes.


17-23: LGTM: Getter and setter for vendor_id are well-implemented.

The get_vendor_id and set_vendor_id methods are correctly implemented, using the get_prop and set_prop methods respectively. The type hinting for the setter ensures that only integers are accepted for the vendor ID.


25-31: LGTM: Getter and setter for trn_id are correctly implemented.

The get_trn_id and set_trn_id methods follow the same pattern as the vendor_id methods, maintaining consistency in the codebase.


49-55: LGTM: Getter and setter for debit are well-implemented.

The get_debit and set_debit methods are correctly implemented, following the established pattern and using appropriate type hinting for the float parameter.


57-63: LGTM: Getter and setter for credit are correctly implemented.

The get_credit and set_credit methods follow the same pattern as the debit methods, maintaining consistency in the codebase and using appropriate type hinting.

Comment on lines 1 to 25
<?php

namespace WeDevs\Dokan\Models\DataStore;

class VendorBalanceStore extends BaseDataStore {

protected function get_fields_with_format(): array {
return [
'vendor_id' => '%d',
'trn_id' => '%d',
'trn_type' => '%d',
'perticulars' => '%s',
'debit' => '%f',
'credit' => '%f',
'status' => '%s',
'trn_date' => '%s',
'balance_date' => '%s',

];
}

public function get_table_name(): string {
return 'dokan_vendor_balance';
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

VendorBalanceStore Class Not Integrated Elsewhere

The VendorBalanceStore class is defined but not utilized in other parts of the codebase. Please ensure that existing references to dokan_vendor_balance are updated to use the new VendorBalanceStore class to maintain consistency and improve maintainability.

🔗 Analysis chain

Overall, the VendorBalanceStore class is well-structured and implements the necessary methods.

The class effectively defines the structure for vendor balance data storage. It properly extends the BaseDataStore class and implements the required methods. The suggestions provided earlier will further enhance its clarity, consistency, and maintainability.

To ensure the VendorBalanceStore class is properly integrated with the rest of the codebase, please run the following verification script:

This script will help identify any potential integration issues or conflicts with existing code.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage and integration of VendorBalanceStore class

# Test 1: Check if VendorBalanceStore is used in other files
echo "Checking usage of VendorBalanceStore:"
rg "VendorBalanceStore" --type php

# Test 2: Verify if BaseDataStore exists and is properly defined
echo "Verifying BaseDataStore:"
rg "abstract class BaseDataStore" --type php

# Test 3: Check for any existing vendor balance related functions or methods
echo "Checking for existing vendor balance related code:"
rg "vendor_balance|dokan_vendor_balance" --type php

Length of output: 4835

Comment on lines 7 to 20
protected function get_fields_with_format(): array {
return [
'vendor_id' => '%d',
'trn_id' => '%d',
'trn_type' => '%d',
'perticulars' => '%s',
'debit' => '%f',
'credit' => '%f',
'status' => '%s',
'trn_date' => '%s',
'balance_date' => '%s',

];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance get_fields_with_format() method for improved clarity and consistency.

Consider the following suggestions:

  1. Add a return type hint for improved type safety.
  2. Use consistent indentation (preferably spaces as per PSR-2).
  3. Consider using more descriptive names for some fields (e.g., 'transaction_id' instead of 'trn_id').

Here's a suggested refactor:

-	protected function get_fields_with_format(): array {
-        return [
-			'vendor_id' => '%d',
-			'trn_id' => '%d',
-			'trn_type' => '%d',
-			'perticulars' => '%s',
-			'debit' => '%f',
-			'credit' => '%f',
-			'status' => '%s',
-			'trn_date' => '%s',
-			'balance_date' => '%s',
-
-        ];
-    }
+    protected function get_fields_with_format(): array {
+        return [
+            'vendor_id' => '%d',
+            'transaction_id' => '%d',
+            'transaction_type' => '%d',
+            'particulars' => '%s',
+            'debit' => '%f',
+            'credit' => '%f',
+            'status' => '%s',
+            'transaction_date' => '%s',
+            'balance_date' => '%s',
+        ];
+    }

Note: Ensure that these field names align with your database schema and other parts of the codebase.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
protected function get_fields_with_format(): array {
return [
'vendor_id' => '%d',
'trn_id' => '%d',
'trn_type' => '%d',
'perticulars' => '%s',
'debit' => '%f',
'credit' => '%f',
'status' => '%s',
'trn_date' => '%s',
'balance_date' => '%s',
];
}
protected function get_fields_with_format(): array {
return [
'vendor_id' => '%d',
'transaction_id' => '%d',
'transaction_type' => '%d',
'particulars' => '%s',
'debit' => '%f',
'credit' => '%f',
'status' => '%s',
'transaction_date' => '%s',
'balance_date' => '%s',
];
}

Comment on lines 47 to 79
/**
* Get a data.
*
* @since DOKAN_SINCE
*
* @param Model $model The model to get.
*
* @return void
* @throw \Exception
*/
// public function get( Model &$model );

/**
* Query data.
*
* @since DOKAN_SINCE
*
* @param array $args
*
* @return array
*/
// public function query( array $args = [] ): array;

/**
* Count data.
*
* @since DOKAN_SINCE
*
* @param array $args
*
* @return int
*/
// public function count( array $args = [] ): int;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Review commented-out methods and correct return type for get.

  1. The get, query, and count methods are currently commented out. If these methods are intended to be part of the interface, they should be uncommented. If they are placeholders for future implementation, consider adding a TODO comment explaining the plan for these methods.

  2. The get method has a void return type, which is unusual for a retrieval method. Consider changing it to return the Model or another appropriate type:

* @return Model The retrieved model.
  1. If you decide to keep these methods commented out, you might want to remove them entirely from the interface to avoid confusion, or add a comment explaining why they are commented out.

Comment on lines 1 to 80
<?php

namespace WeDevs\Dokan\Models;

class VendorBalance extends BaseModel {

// 'vendor_id' => '%d',
// 'trn_id' => '%d',
// 'trn_type' => '%d',
// 'perticulars' => '%s',
// 'debit' => '%f',
// 'credit' => '%f',
// 'status' => '%s',
// 'trn_date' => '%s',
// 'balance_date' => '%s'

public function get_vendor_id( string $context = 'view' ) {
return $this->get_prop( 'vendor_id', $context );
}

public function set_vendor_id( int $id ) {
return $this->set_prop( 'vendor_id', $id );
}

public function get_trn_id( string $context = 'view' ) {
return $this->get_prop( 'trn_id', $context );
}

public function set_trn_id( int $id ) {
return $this->set_prop( 'trn_id', $id );
}

public function get_trn_type( string $context = 'view' ) {
return $this->get_prop( 'trn_type', $context );
}

public function set_trn_type( string $type ) {
return $this->set_prop( 'trn_type', $type );
}

public function get_particulars( string $context = 'view' ) {
return $this->get_prop( 'perticulars', $context );
}

public function set_particulars( string $note ) {
return $this->set_prop( 'perticulars', $note );
}

public function get_debit( string $context = 'view' ) {
return $this->get_prop( 'debit', $context );
}

public function set_debit( float $amount ) {
return $this->set_prop( 'debit', $amount );
}

public function get_credit( string $context = 'view' ) {
return $this->get_prop( 'credit', $context );
}

public function set_credit( float $amount ) {
return $this->set_prop( 'credit', $amount );
}

public function get_trn_date( string $context = 'view' ) {
return $this->get_prop( 'trn_date', $context );
}

public function set_trn_date( float $amount ) {
return $this->set_date_prop( 'trn_date', $amount );
}

public function get_balance_date( string $context = 'view' ) {
return $this->get_prop( 'balance_date', $context );
}

public function set_balance_date( float $amount ) {
return $this->set_date_prop( 'balance_date', $amount );
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Overall class structure review and suggestions for improvement.

The VendorBalance class is well-structured and implements getters and setters for most properties consistently. However, there are a few areas for improvement:

  1. The 'status' property mentioned in the comments doesn't have corresponding getter and setter methods. Consider adding these for completeness.
  2. There's no constructor or any custom logic beyond getters and setters. Consider if any initialization or validation logic is needed.
  3. PHPDoc is missing for the class and its methods. Adding comprehensive documentation would improve code readability and maintainability.

Consider the following improvements:

  1. Add getter and setter methods for the 'status' property.
  2. Implement a constructor if any initialization is required.
  3. Add PHPDoc comments for the class and all methods.

Example of adding PHPDoc:

/**
 * Class VendorBalance
 * 
 * Represents a vendor's balance record in the Dokan plugin.
 */
class VendorBalance extends BaseModel {
    /**
     * Get the vendor ID.
     *
     * @param string $context Context of the request.
     * @return int The vendor ID.
     */
    public function get_vendor_id( string $context = 'view' ) {
        return $this->get_prop( 'vendor_id', $context );
    }

    // Add similar PHPDoc for other methods...
}

Comment on lines 65 to 71
public function get_trn_date( string $context = 'view' ) {
return $this->get_prop( 'trn_date', $context );
}

public function set_trn_date( float $amount ) {
return $this->set_date_prop( 'trn_date', $amount );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct parameter type for set_trn_date method.

The get_trn_date method is implemented correctly. However, there's an issue with the set_trn_date method:

  1. It correctly uses set_date_prop for date handling.
  2. But it expects a float parameter, which is inconsistent with typical date representations.

Please update the set_trn_date method to accept a more appropriate date format:

- public function set_trn_date( float $amount ) {
-     return $this->set_date_prop( 'trn_date', $amount );
+ public function set_trn_date( string $date ) {
+     return $this->set_date_prop( 'trn_date', $date );

Consider using a string parameter that can be parsed into a date, or a DateTime object if your system supports it.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function get_trn_date( string $context = 'view' ) {
return $this->get_prop( 'trn_date', $context );
}
public function set_trn_date( float $amount ) {
return $this->set_date_prop( 'trn_date', $amount );
}
public function get_trn_date( string $context = 'view' ) {
return $this->get_prop( 'trn_date', $context );
}
public function set_trn_date( string $date ) {
return $this->set_date_prop( 'trn_date', $date );
}

Comment on lines 41 to 47
public function get_particulars( string $context = 'view' ) {
return $this->get_prop( 'perticulars', $context );
}

public function set_particulars( string $note ) {
return $this->set_prop( 'perticulars', $note );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct spelling inconsistency for 'particulars'.

The methods get_particulars and set_particulars are implemented correctly. However, there's a spelling inconsistency between the property name in the comments ('perticulars') and the method names ('particulars').

Please update the property comment to use the correct spelling:

- //      'perticulars' => '%s',
+ //      'particulars' => '%s',

Also, ensure that this spelling is consistent throughout the codebase and database schema.

Committable suggestion was skipped due to low confidence.

abstract protected function get_fields_with_format(): array;
abstract public function get_table_name(): string;

public function create( BaseModel &$model ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove unnecessary passing of objects by reference

In the methods create(), read(), update(), and delete(), the BaseModel object is passed by reference using &$model. In PHP, objects are already passed by reference by default, so the ampersand & is unnecessary and may cause confusion.

Apply this diff to remove the unnecessary reference operator:

-    public function create( BaseModel &$model ) {
+    public function create( BaseModel $model ) {

-    public function read( BaseModel &$model ) {
+    public function read( BaseModel $model ) {

-    public function update( BaseModel &$model ) {
+    public function update( BaseModel $model ) {

-    public function delete( BaseModel &$model, $args = array() ) {
+    public function delete( BaseModel $model, $args = array() ) {

Also applies to: 42-42, 87-87, 117-117

Comment on lines +215 to +217
if ( ! str_starts_with( $table_name, $wpdb->prefix ) ) {
$table_name = $wpdb->prefix . $table_name;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure compatibility with PHP versions when using str_starts_with()

The function str_starts_with() is available from PHP 8.0 onwards. If the project needs to support earlier PHP versions, using this function will cause compatibility issues.

Replace str_starts_with() with an alternative that works with older PHP versions:

-        if ( ! str_starts_with( $table_name, $wpdb->prefix ) ) {
+        if ( strpos( $table_name, $wpdb->prefix ) !== 0 ) {

This change maintains the same functionality while ensuring compatibility with PHP versions prior to 8.0.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ( ! str_starts_with( $table_name, $wpdb->prefix ) ) {
$table_name = $wpdb->prefix . $table_name;
}
if ( strpos( $table_name, $wpdb->prefix ) !== 0 ) {
$table_name = $wpdb->prefix . $table_name;
}

Comment on lines 140 to 143
"DELETE FROM {$table_name}
WHERE $id_field_name = $format",
$id
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential SQL injection vulnerability in deletion query

In the delete_by_id() method, variables are interpolated directly into the SQL string, which poses a security risk. The variables {$table_name}, $id_field_name, and $format should not be directly interpolated into the query.

Refactor the code to use $wpdb->delete() for safer query execution:

-            $result = $wpdb->query(
-                $wpdb->prepare(
-                    "DELETE FROM {$table_name}
-                    WHERE $id_field_name = $format",
-                    $id
-                )
-            );
+            $result = $wpdb->delete(
+                $table_name,
+                array( $id_field_name => $id ),
+                array( $format )
+            );

This approach ensures that the table name and where clause are handled safely, preventing SQL injection.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"DELETE FROM {$table_name}
WHERE $id_field_name = $format",
$id
)
$result = $wpdb->delete(
$table_name,
array( $id_field_name => $id ),
array( $format )
);
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 140-140:
Use placeholders and $wpdb->prepare(); found interpolated variable {$table_name} at "DELETE FROM {$table_name}


[failure] 141-141:
Use placeholders and $wpdb->prepare(); found interpolated variable $id_field_name at WHERE $id_field_name = $format"


[failure] 141-141:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at WHERE $id_field_name = $format"


[warning] 141-141:
Replacement variables found, but no valid placeholders found in the query.

Comment on lines 63 to 66
"{$id_field_name} = $format",
$model->get_id()
)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential SQL injection vulnerability in query preparation

The SQL query in the read() method uses interpolated variables within the query string, which can lead to SQL injection vulnerabilities. Specifically, the variables {$id_field_name} and $format are directly interpolated.

Apply this diff to properly prepare the SQL statement using placeholders:

             $this->add_sql_clause(
                 'where',
-                $wpdb->prepare(
-                    "{$id_field_name} = $format",
+                $wpdb->prepare(
+                    "{$id_field_name} = %s",
                     $model->get_id()
                 )
             );

Note: Since $id_field_name is a column name, ensure it is sanitized and originates from a trusted source. Consider using a whitelist of allowed field names.

Committable suggestion was skipped due to low confidence.

🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 63-63:
Use placeholders and $wpdb->prepare(); found interpolated variable {$id_field_name} at "{$id_field_name} = $format"


[failure] 63-63:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at "{$id_field_name} = $format"


[warning] 63-63:
Replacement variables found, but no valid placeholders found in the query.

@mrabbani mrabbani added In Progress The issues is being worked on and removed DO NOT MERGE Don't merge this PR labels Oct 24, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🧹 Outside diff range and nitpick comments (7)
includes/Models/DataStore/VendorBalanceStore.php (3)

5-6: Remove unused import.

The BaseModel class is imported but never used directly in this file.

-use WeDevs\Dokan\Models\BaseModel;
 use WeDevs\Dokan\Models\VendorBalance;

8-8: Add class-level documentation.

Please add a PHPDoc block describing the purpose and responsibility of this class.

+/**
+ * Class VendorBalanceStore
+ *
+ * Handles the storage and retrieval of vendor balance data in the database.
+ * Implements data persistence operations for the VendorBalance model.
+ */
 class VendorBalanceStore extends BaseDataStore {

24-26: Add documentation and consider using table prefix.

  1. Add PHPDoc for the method
  2. Consider using WordPress table prefix for better compatibility with different installations
+	/**
+	 * Get the database table name for vendor balance records.
+	 *
+	 * @return string The prefixed table name
+	 */
 	public function get_table_name(): string {
-        return 'dokan_vendor_balance';
+        global $wpdb;
+        return $wpdb->prefix . 'dokan_vendor_balance';
     }
tests/php/src/Models/VendorBalanceModelTest.php (1)

8-15: Consider enhancing PHPDoc with @SInCE tag.

Adding version information helps track when this test was introduced.

     /**
      * Indicates if the test is a unit test.
      *
      * @var bool
+     * @since 3.x.x
      */
includes/Models/DataStore/DataStoreInterface.php (1)

7-12: Enhance interface documentation.

  1. Replace the DOKAN_SINCE placeholder with the actual version number.
  2. Add more detailed description about:
    • The purpose of this interface
    • Its role in the data persistence layer
    • Expected implementation contexts
 /**
- * Data Store interface.
+ * Defines the contract for Dokan data persistence operations.
+ *
+ * This interface standardizes how Dokan models interact with the underlying
+ * storage layer, providing a consistent API for CRUD operations.
  *
- * @since DOKAN_SINCE
+ * @since 3.7.0
  */
includes/Models/VendorBalance.php (1)

31-41: Add property type definitions and validation rules in comments.

The data array properties should include type definitions and any validation rules that apply. This helps with code maintenance and prevents invalid data.

Example addition:

/**
 * The default data of the object.
 *
 * @var array{
 *     vendor_id: int,          // Positive integer
 *     trn_id: int,            // Positive integer
 *     trn_type: string,       // One of TRN_TYPE_* constants
 *     perticulars: string,    // Non-empty string
 *     debit: float,           // Non-negative float
 *     credit: float,          // Non-negative float
 *     status: string,         // Valid status values
 *     trn_date: string,       // Format: Y-m-d H:i:s
 *     balance_date: string    // Format: Y-m-d H:i:s
 * }
 */
includes/Models/DataStore/BaseDataStore.php (1)

57-97: Consider implementing caching for read operations

The read method could benefit from caching to improve performance, especially for frequently accessed records.

Consider implementing WordPress object caching:

protected function get_cache_key( $id ): string {
    return "dokan_store_{$this->get_table_name()}_{$id}";
}

public function read( BaseModel &$model ) {
    $id = $model->get_id();
    $cache_key = $this->get_cache_key( $id );
    
    // Try to get from cache first
    $data = wp_cache_get( $cache_key );
    if ( false !== $data ) {
        $model->set_props( $data );
        $model->set_object_read( true );
        return $data;
    }
    
    // Existing read logic here...
    
    // Cache the result
    wp_cache_set( $cache_key, $this->format_raw_data( $raw_item ) );
}

Remember to invalidate the cache in create/update/delete operations.

🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 78-78:
Use placeholders and $wpdb->prepare(); found interpolated variable {$id_field_name} at "{$id_field_name} = $format"


[failure] 78-78:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at "{$id_field_name} = $format"


[warning] 78-78:
Replacement variables found, but no valid placeholders found in the query.


[warning] 84-84:
Use placeholders and $wpdb->prepare(); found $this


[warning] 84-84:
Use placeholders and $wpdb->prepare(); found get_query_statement

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 40e5ecb and 7e681f9.

📒 Files selected for processing (5)
  • includes/Models/DataStore/BaseDataStore.php (1 hunks)
  • includes/Models/DataStore/DataStoreInterface.php (1 hunks)
  • includes/Models/DataStore/VendorBalanceStore.php (1 hunks)
  • includes/Models/VendorBalance.php (1 hunks)
  • tests/php/src/Models/VendorBalanceModelTest.php (1 hunks)
🧰 Additional context used
🪛 GitHub Check: Run PHPCS inspection
includes/Models/DataStore/BaseDataStore.php

[failure] 78-78:
Use placeholders and $wpdb->prepare(); found interpolated variable {$id_field_name} at "{$id_field_name} = $format"


[failure] 78-78:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at "{$id_field_name} = $format"


[warning] 78-78:
Replacement variables found, but no valid placeholders found in the query.


[warning] 84-84:
Use placeholders and $wpdb->prepare(); found $this


[warning] 84-84:
Use placeholders and $wpdb->prepare(); found get_query_statement


[failure] 154-154:
Use placeholders and $wpdb->prepare(); found interpolated variable {$table_name} at "DELETE FROM {$table_name}


[failure] 155-155:
Use placeholders and $wpdb->prepare(); found interpolated variable $id_field_name at WHERE $id_field_name = $format"


[failure] 155-155:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at WHERE $id_field_name = $format"


[warning] 155-155:
Replacement variables found, but no valid placeholders found in the query.

🔇 Additional comments (4)
includes/Models/DataStore/DataStoreInterface.php (2)

1-6: LGTM! File structure follows PSR-4 standards.


60-93: ⚠️ Potential issue

Implement or remove commented methods.

The commented-out methods appear to be essential for a complete data store interface. Either:

  1. Implement them as part of the interface, or
  2. Remove them if they're not needed in the current version

If these methods are planned for future implementation, track them in the project's issue tracker instead of keeping them as commented code.

includes/Models/VendorBalance.php (2)

1-10: LGTM! Well-structured class definition with clear transaction type constants.

The class structure follows proper PHP conventions with appropriate namespace, imports, and constant definitions.


1-230: Verify adherence to WordPress coding standards.

Let's verify if the implementation follows WordPress coding standards and common patterns.

✅ Verification successful

Based on the shell script results, I can now provide a comprehensive analysis of the implementation against WordPress coding standards.

Implementation follows WordPress coding standards and best practices

The VendorBalance model implementation aligns well with WordPress and WooCommerce coding standards:

  1. Proper use of namespacing with WeDevs\Dokan\Models
  2. Clear documentation with PHPDoc blocks for all methods
  3. Consistent method naming following WordPress conventions (e.g. get_, set_)
  4. Type declarations and return type hints used appropriately
  5. Proper data sanitization through parent BaseModel class
  6. Integration with WordPress hooks system as evidenced by the codebase
  7. Follows similar patterns seen in WooCommerce core for model implementations

The implementation also demonstrates good practices:

  • Clear separation of concerns with dedicated data store class
  • Immutable constants for transaction types
  • Protected properties for internal data
  • Consistent return type handling
  • Proper validation in setters

The code integrates well with the broader Dokan plugin architecture and follows established WordPress development patterns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for WordPress coding standards patterns
echo "Checking for WordPress coding standards..."

# Check if similar models exist in WordPress core
echo "Checking for similar model implementations in WordPress core..."
rg -l "extends \w+Model" --type php

# Check for common WordPress filter/action hooks pattern
echo "Checking for filter/action hooks..."
rg "do_action|apply_filters" --type php

# Look for similar balance/transaction handling in WordPress
echo "Checking for similar balance handling implementations..."
rg -l "balance|transaction" --type php

Length of output: 105975

Comment on lines +35 to +37
protected function get_perticulars( VendorBalance $model, string $context = 'edit' ): string {
return $model->get_particulars( $context );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix spelling inconsistency: "perticulars" vs "particulars".

The method name uses "perticulars" while the documentation and likely the model method uses "particulars". Maintain consistent spelling across the codebase.

-	protected function get_perticulars( VendorBalance $model, string $context = 'edit' ): string {
-		return $model->get_particulars( $context );
+	protected function get_particulars( VendorBalance $model, string $context = 'edit' ): string {
+		return $model->get_particulars( $context );

Also update the field name in get_fields_with_format():

-			'perticulars' => '%s',
+			'particulars' => '%s',

Committable suggestion was skipped due to low confidence.

return [
'vendor_id' => '%d',
'trn_id' => '%d',
'trn_type' => '%s',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix format specifier for transaction type field.

The field name trn_type suggests a numeric type, but the format specifier is %s (string). If this is meant to be numeric, use %d instead.

-			'trn_type' => '%s',
+			'trn_type' => '%d',

Committable suggestion was skipped due to low confidence.

Comment on lines 8 to 63
class VendorBalanceModelTest extends DokanTestCase {
/**
* Indicates if the test is a unit test.
*
* @var bool
*/
protected $is_unit_test = true;

public function test_save_method() {
$vendor_balance = new VendorBalance();
$vendor_balance->set_debit( 100 );
$vendor_balance->set_trn_id( 1 );
$vendor_balance->set_trn_type( VendorBalance::TRN_TYPE_DOKAN_ORDERS );
$vendor_balance->set_particulars( 'test' );
$vendor_balance->save();

$this->assertNotEmpty( $vendor_balance->get_id() );
$this->assertDatabaseCount(
'dokan_vendor_balance', 1, [
'id' => $vendor_balance->get_id(),
// 'debit' => 100,
'trn_id' => 1,
'trn_type' => VendorBalance::TRN_TYPE_DOKAN_ORDERS,
]
);
}

public function test_update_method() {
$vendor_balance = new VendorBalance();
$vendor_balance->save();

$this->assertEmpty( $vendor_balance->get_particulars() );

// Change the particulars
$vendor_balance->set_particulars( 'test123' );
$vendor_balance->save();

// Assert changes are applied to the database.
$this->assertDatabaseHas(
'dokan_vendor_balance', [
'perticulars' => 'test123',
]
);
}

public function test_delete_method() {
$vendor_balance = new VendorBalance();
$vendor_balance->save();
$id = $vendor_balance->get_id();
$this->assertNotEmpty( $vendor_balance->get_id() );

$vendor_balance->delete();

$this->assertDatabaseCount( 'dokan_vendor_balance', 0, [ 'id' => $id ] );
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding more test cases for better coverage.

The current test suite covers basic CRUD operations but lacks tests for:

  1. Invalid data handling (e.g., negative amounts)
  2. Edge cases (e.g., maximum values)
  3. Error conditions (e.g., duplicate transactions)

Would you like me to provide examples of these additional test cases?

Comment on lines +16 to +33
public function test_save_method() {
$vendor_balance = new VendorBalance();
$vendor_balance->set_debit( 100 );
$vendor_balance->set_trn_id( 1 );
$vendor_balance->set_trn_type( VendorBalance::TRN_TYPE_DOKAN_ORDERS );
$vendor_balance->set_particulars( 'test' );
$vendor_balance->save();

$this->assertNotEmpty( $vendor_balance->get_id() );
$this->assertDatabaseCount(
'dokan_vendor_balance', 1, [
'id' => $vendor_balance->get_id(),
// 'debit' => 100,
'trn_id' => 1,
'trn_type' => VendorBalance::TRN_TYPE_DOKAN_ORDERS,
]
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Several improvements needed in save method test.

  1. The debit amount assertion is commented out
  2. Missing vendor_id test case
  3. Database assertions could be more comprehensive
  4. Missing PHPDoc for the test method

Apply these improvements:

+    /**
+     * Test the save operation of VendorBalance model
+     *
+     * @since 3.x.x
+     * @return void
+     */
     public function test_save_method() {
         $vendor_balance = new VendorBalance();
+        $vendor_balance->set_vendor_id( 1 );
         $vendor_balance->set_debit( 100 );
         $vendor_balance->set_trn_id( 1 );
         $vendor_balance->set_trn_type( VendorBalance::TRN_TYPE_DOKAN_ORDERS );
         $vendor_balance->set_particulars( 'test' );
         $vendor_balance->save();

         $this->assertNotEmpty( $vendor_balance->get_id() );
         $this->assertDatabaseCount(
             'dokan_vendor_balance', 1, [
                 'id' => $vendor_balance->get_id(),
-                // 'debit' => 100,
+                'vendor_id' => 1,
+                'debit' => 100.00,
                 'trn_id' => 1,
                 'trn_type' => VendorBalance::TRN_TYPE_DOKAN_ORDERS,
+                'particulars' => 'test',
             ]
         );
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function test_save_method() {
$vendor_balance = new VendorBalance();
$vendor_balance->set_debit( 100 );
$vendor_balance->set_trn_id( 1 );
$vendor_balance->set_trn_type( VendorBalance::TRN_TYPE_DOKAN_ORDERS );
$vendor_balance->set_particulars( 'test' );
$vendor_balance->save();
$this->assertNotEmpty( $vendor_balance->get_id() );
$this->assertDatabaseCount(
'dokan_vendor_balance', 1, [
'id' => $vendor_balance->get_id(),
// 'debit' => 100,
'trn_id' => 1,
'trn_type' => VendorBalance::TRN_TYPE_DOKAN_ORDERS,
]
);
}
/**
* Test the save operation of VendorBalance model
*
* @since 3.x.x
* @return void
*/
public function test_save_method() {
$vendor_balance = new VendorBalance();
$vendor_balance->set_vendor_id( 1 );
$vendor_balance->set_debit( 100 );
$vendor_balance->set_trn_id( 1 );
$vendor_balance->set_trn_type( VendorBalance::TRN_TYPE_DOKAN_ORDERS );
$vendor_balance->set_particulars( 'test' );
$vendor_balance->save();
$this->assertNotEmpty( $vendor_balance->get_id() );
$this->assertDatabaseCount(
'dokan_vendor_balance', 1, [
'id' => $vendor_balance->get_id(),
'vendor_id' => 1,
'debit' => 100.00,
'trn_id' => 1,
'trn_type' => VendorBalance::TRN_TYPE_DOKAN_ORDERS,
'particulars' => 'test',
]
);
}

Comment on lines +25 to +34
/**
* Method to read a download permission from the database.
*
* @param BaseModel $model BaseModel object.
*
* @phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
*
* @throws Exception Throw exception if invalid entity is passed.
*/
public function read( BaseModel &$model );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect documentation and PHPCS ignore comment.

  1. The PHPDoc mentions "download permission" which seems copied from another context.
  2. The PHPCS ignore comment needs justification or should be removed.
     /**
-     * Method to read a download permission from the database.
+     * Reads model data from the database.
      *
      * @param BaseModel $model BaseModel object.
      *
-     * @phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+     * @throws \Exception If invalid entity is passed.
      */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Method to read a download permission from the database.
*
* @param BaseModel $model BaseModel object.
*
* @phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
*
* @throws Exception Throw exception if invalid entity is passed.
*/
public function read( BaseModel &$model );
/**
* Reads model data from the database.
*
* @param BaseModel $model BaseModel object.
*
* @throws \Exception If invalid entity is passed.
*/
public function read( BaseModel &$model );

Comment on lines 107 to 109
public function set_trn_type( string $type ) {
return $this->set_prop( 'trn_type', $type );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add transaction type validation.

The set_trn_type method should validate that the input matches one of the defined transaction type constants.

 public function set_trn_type( string $type ) {
+    $valid_types = [
+        self::TRN_TYPE_DOKAN_ORDERS,
+        self::TRN_TYPE_DOKAN_WITHDRAW,
+        self::TRN_TYPE_DOKAN_REFUND,
+    ];
+    if ( ! in_array( $type, $valid_types, true ) ) {
+        throw new \InvalidArgumentException( 'Invalid transaction type.' );
+    }
     return $this->set_prop( 'trn_type', $type );
 }

Committable suggestion was skipped due to low confidence.

namespace WeDevs\Dokan\Models\DataStore;

use Automattic\WooCommerce\Admin\API\Reports\SqlQuery;
use Automattic\WooCommerce\Pinterest\API\Base;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unused import

The Automattic\WooCommerce\Pinterest\API\Base import appears to be unused in this class and should be removed.

-use Automattic\WooCommerce\Pinterest\API\Base;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
use Automattic\WooCommerce\Pinterest\API\Base;

Comment on lines 220 to 228
protected function format_raw_data( $raw_data ): array {
$data = array();

foreach ( $this->get_fields() as $db_field_name ) {
$data[ $db_field_name ] = $raw_data->{$db_field_name};
}

return apply_filters( $this->get_hook_prefix() . 'format_raw_data', $data, $raw_data );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add property existence check in format_raw_data()

The method directly accesses object properties without checking their existence, which could lead to PHP notices or errors.

     protected function format_raw_data( $raw_data ): array {
         $data = array();
 
         foreach ( $this->get_fields() as $db_field_name ) {
-            $data[ $db_field_name ] = $raw_data->{$db_field_name};
+            $data[ $db_field_name ] = property_exists( $raw_data, $db_field_name ) 
+                ? $raw_data->{$db_field_name} 
+                : null;
         }
 
         return apply_filters( $this->get_hook_prefix() . 'format_raw_data', $data, $raw_data );
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
protected function format_raw_data( $raw_data ): array {
$data = array();
foreach ( $this->get_fields() as $db_field_name ) {
$data[ $db_field_name ] = $raw_data->{$db_field_name};
}
return apply_filters( $this->get_hook_prefix() . 'format_raw_data', $data, $raw_data );
}
protected function format_raw_data( $raw_data ): array {
$data = array();
foreach ( $this->get_fields() as $db_field_name ) {
$data[ $db_field_name ] = property_exists( $raw_data, $db_field_name )
? $raw_data->{$db_field_name}
: null;
}
return apply_filters( $this->get_hook_prefix() . 'format_raw_data', $data, $raw_data );
}

Comment on lines 57 to 97
public function read( BaseModel &$model ) {
global $wpdb;

if ( ! $model->get_id() ) {
$message = $this->get_hook_prefix() . ' : ' . __( 'Invalid entity item.', 'dokan-lite' );

throw new Exception( esc_html( $message ) );
}

$model->set_defaults();

$id_field_name = $this->get_id_field_name();
$format = $this->get_id_field_format();

$this->clear_all_clauses();
$this->add_sql_clause( 'select', $this->get_selected_columns() );
$this->add_sql_clause( 'from', $this->get_table_name_with_prefix() );

$this->add_sql_clause(
'where',
$wpdb->prepare(
"{$id_field_name} = $format",
$model->get_id()
)
);

$raw_item = $wpdb->get_row(
$this->get_query_statement()
);

if ( ! $raw_item ) {
$message = $this->get_hook_prefix() . ' : ' . __( 'Invalid entity item.', 'dokan-lite' );

throw new Exception( esc_html( $message ) );
}

$model->set_props( $this->format_raw_data( $raw_item ) );
$model->set_object_read( true );

return $raw_item;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix SQL injection vulnerability in read() method

The SQL query construction in the read method is vulnerable to SQL injection. While the ID value is properly prepared, the field name and format are directly interpolated into the query string.

-            $wpdb->prepare(
-                "{$id_field_name} = $format",
-                $model->get_id()
-            )
+            $wpdb->prepare(
+                $wpdb->prepare( '%s = %d', $id_field_name, $model->get_id() )
+            )

Additionally, consider validating $id_field_name against a whitelist of allowed column names.

Committable suggestion was skipped due to low confidence.

🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 78-78:
Use placeholders and $wpdb->prepare(); found interpolated variable {$id_field_name} at "{$id_field_name} = $format"


[failure] 78-78:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at "{$id_field_name} = $format"


[warning] 78-78:
Replacement variables found, but no valid placeholders found in the query.


[warning] 84-84:
Use placeholders and $wpdb->prepare(); found $this


[warning] 84-84:
Use placeholders and $wpdb->prepare(); found get_query_statement

Comment on lines +240 to +250
protected function get_table_name_with_prefix(): string {
global $wpdb;

$table_name = $this->get_table_name();

if ( ! str_starts_with( $table_name, $wpdb->prefix ) ) {
$table_name = $wpdb->prefix . $table_name;
}

return $table_name;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add table name validation

The table name should be validated to prevent potential security issues and ensure it follows WordPress naming conventions.

     protected function get_table_name_with_prefix(): string {
         global $wpdb;
 
         $table_name = $this->get_table_name();
 
+        // Validate table name format
+        if ( ! preg_match('/^[a-zA-Z0-9_]+$/', $table_name) ) {
+            throw new Exception( __( 'Invalid table name format', 'dokan-lite' ) );
+        }
+
         if ( ! str_starts_with( $table_name, $wpdb->prefix ) ) {
             $table_name = $wpdb->prefix . $table_name;
         }
 
         return $table_name;
     }

Committable suggestion was skipped due to low confidence.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Outside diff range and nitpick comments (1)
includes/Models/VendorBalance.php (1)

7-10: Add class-level PHPDoc documentation.

The class is missing comprehensive PHPDoc documentation. Add a description of the class purpose, responsibilities, and usage.

+/**
+ * Class VendorBalance
+ *
+ * Represents a vendor's balance record in the Dokan plugin.
+ * This class manages financial transactions including orders, withdrawals, and refunds
+ * for vendors, tracking their credits, debits, and overall balance.
+ *
+ * @since 1.0.0
+ * @package WeDevs\Dokan\Models
+ */
 class VendorBalance extends BaseModel {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 7e681f9 and 73b289d.

📒 Files selected for processing (4)
  • includes/Models/BaseModel.php (1 hunks)
  • includes/Models/DataStore/BaseDataStore.php (1 hunks)
  • includes/Models/VendorBalance.php (1 hunks)
  • tests/php/src/Models/VendorBalanceModelTest.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • includes/Models/BaseModel.php
  • tests/php/src/Models/VendorBalanceModelTest.php
🧰 Additional context used
🪛 GitHub Check: Run PHPCS inspection
includes/Models/DataStore/BaseDataStore.php

[failure] 78-78:
Use placeholders and $wpdb->prepare(); found interpolated variable {$id_field_name} at " AND {$id_field_name} = $format"


[failure] 78-78:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at " AND {$id_field_name} = $format"


[warning] 78-78:
Replacement variables found, but no valid placeholders found in the query.


[warning] 84-84:
Use placeholders and $wpdb->prepare(); found $this


[warning] 84-84:
Use placeholders and $wpdb->prepare(); found get_query_statement


[failure] 154-154:
Use placeholders and $wpdb->prepare(); found interpolated variable {$table_name} at "DELETE FROM {$table_name}


[failure] 155-155:
Use placeholders and $wpdb->prepare(); found interpolated variable $id_field_name at WHERE $id_field_name = $format"


[failure] 155-155:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at WHERE $id_field_name = $format"


[warning] 155-155:
Replacement variables found, but no valid placeholders found in the query.

Comment on lines 146 to 148
protected function set_perticulars( string $note ) {
return $this->set_particulars( $note );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove redundant protected method set_perticulars.

The protected method set_perticulars is redundant as it:

  1. Simply calls the public set_particulars method
  2. Has the same misspelling issue
  3. Adds unnecessary complexity to the codebase

Remove the entire method as it provides no additional functionality:

-    /**
-     * Set the perticulars (note) for the transaction.
-     *
-     * @param string $note The note to be stored.
-     * @return void
-     */
-    protected function set_perticulars( string $note ) {
-        return $this->set_particulars( $note );
-    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
protected function set_perticulars( string $note ) {
return $this->set_particulars( $note );
}

Comment on lines +126 to +128
public function get_particulars( string $context = 'view' ): string {
return (string) $this->get_prop( 'perticulars', $context );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix property name in get_particulars method.

The method uses the misspelled property name 'perticulars' which should be 'particulars' to match the method name.

 public function get_particulars( string $context = 'view' ): string {
-    return (string) $this->get_prop( 'perticulars', $context );
+    return (string) $this->get_prop( 'particulars', $context );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function get_particulars( string $context = 'view' ): string {
return (string) $this->get_prop( 'perticulars', $context );
}
public function get_particulars( string $context = 'view' ): string {
return (string) $this->get_prop( 'particulars', $context );
}

Comment on lines +31 to +41
protected $data = [
'vendor_id' => 0,
'trn_id' => 0,
'trn_type' => '',
'perticulars' => '',
'debit' => 0,
'credit' => 0,
'status' => '',
'trn_date' => '',
'balance_date' => '',
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix spelling in data property name.

The property 'perticulars' is misspelled in the data array (should be 'particulars'). This inconsistency could cause issues as the getter method uses the correct spelling.

 protected $data = [
     'vendor_id' => 0,
     'trn_id' => 0,
     'trn_type' => '',
-    'perticulars' => '',
+    'particulars' => '',
     'debit' => 0,
     'credit' => 0,
     'status' => '',
     'trn_date' => '',
     'balance_date' => '',
 ];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
protected $data = [
'vendor_id' => 0,
'trn_id' => 0,
'trn_type' => '',
'perticulars' => '',
'debit' => 0,
'credit' => 0,
'status' => '',
'trn_date' => '',
'balance_date' => '',
];
protected $data = [
'vendor_id' => 0,
'trn_id' => 0,
'trn_type' => '',
'particulars' => '',
'debit' => 0,
'credit' => 0,
'status' => '',
'trn_date' => '',
'balance_date' => '',
];

if ( ! $model->get_id() ) {
$message = $this->get_hook_prefix() . ' : ' . __( 'Invalid entity item.', 'dokan-lite' );

throw new Exception( esc_html( $message ) );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid using esc_html() in exception messages

The esc_html() function is intended for escaping output in HTML contexts, not for internal exception messages. Using it here is unnecessary and may lead to confusion.

Remove esc_html() from the exception messages:

             if ( ! $model->get_id() ) {
                 $message = $this->get_hook_prefix() . ' : ' . __( 'Invalid entity item.', 'dokan-lite' );

-                throw new Exception( esc_html( $message ) );
+                throw new Exception( $message );
             }

---

             if ( ! $raw_item ) {
                 $message = $this->get_hook_prefix() . ' : ' . __( 'Invalid entity item.', 'dokan-lite' );

-                throw new Exception( esc_html( $message ) );
+                throw new Exception( $message );
             }

Exception messages should be plain strings since they are not rendered in a browser context.

Also applies to: 90-90


$table_name = $this->get_table_name();

if ( ! str_starts_with( $table_name, $wpdb->prefix ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure PHP version compatibility when using str_starts_with()

The function str_starts_with() is available in PHP 8.0 and later. If the project needs to support earlier PHP versions, using this function will cause compatibility issues.

Replace str_starts_with() with an alternative compatible with older PHP versions:

         protected function get_table_name_with_prefix(): string {
             global $wpdb;

             $table_name = $this->get_table_name();

-            if ( ! str_starts_with( $table_name, $wpdb->prefix ) ) {
+            if ( strpos( $table_name, $wpdb->prefix ) !== 0 ) {
                 $table_name = $wpdb->prefix . $table_name;
             }

             return $table_name;
         }

This change maintains the same functionality while ensuring compatibility with PHP versions prior to 8.0.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ( ! str_starts_with( $table_name, $wpdb->prefix ) ) {
if ( strpos( $table_name, $wpdb->prefix ) !== 0 ) {

Comment on lines 152 to 158
$result = $wpdb->query(
$wpdb->prepare(
"DELETE FROM {$table_name}
WHERE $id_field_name = $format",
$id
)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential SQL injection vulnerability in delete_by_id() method

In the delete_by_id() method, variables are interpolated directly into the SQL query. Injecting {$table_name}, $id_field_name, and $format into the query string can lead to SQL injection vulnerabilities.

Refactor the code to use $wpdb->delete() for safer query execution:

             $table_name = $this->get_table_name_with_prefix();
             $id_field_name = $this->get_id_field_name();
             $format = $this->get_id_field_format();

-            $result = $wpdb->query(
-                $wpdb->prepare(
-                    "DELETE FROM {$table_name}
-                    WHERE $id_field_name = $format",
-                    $id
-                )
-            );
+            $result = $wpdb->delete(
+                $table_name,
+                [ $id_field_name => $id ],
+                [ $format ]
+            );

Using $wpdb->delete() ensures that the table name and where clause are handled safely, preventing SQL injection vulnerabilities. Ensure that $table_name and $id_field_name are sanitized or come from trusted sources.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$result = $wpdb->query(
$wpdb->prepare(
"DELETE FROM {$table_name}
WHERE $id_field_name = $format",
$id
)
);
$result = $wpdb->delete(
$table_name,
[ $id_field_name => $id ],
[ $format ]
);
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 154-154:
Use placeholders and $wpdb->prepare(); found interpolated variable {$table_name} at "DELETE FROM {$table_name}


[failure] 155-155:
Use placeholders and $wpdb->prepare(); found interpolated variable $id_field_name at WHERE $id_field_name = $format"


[failure] 155-155:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at WHERE $id_field_name = $format"


[warning] 155-155:
Replacement variables found, but no valid placeholders found in the query.

Comment on lines +77 to +81
$wpdb->prepare(
" AND {$id_field_name} = $format",
$model->get_id()
)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential SQL injection vulnerability in read() method

In the read() method, variables are being interpolated directly into the SQL query. Specifically, {$id_field_name} and $format are injected into the query string, which can lead to SQL injection vulnerabilities.

Refactor the code to safely include the field name and use proper placeholders:

             $this->clear_all_clauses();
             $this->add_sql_clause( 'select', $this->get_selected_columns() );
             $this->add_sql_clause( 'from', $this->get_table_name_with_prefix() );

-            $this->add_sql_clause(
-                'where',
-                $wpdb->prepare(
-                    " AND {$id_field_name} = $format",
-                    $model->get_id()
-                )
-            );
+            $id_field_name_safe = esc_sql( $id_field_name );
+            $this->add_sql_clause(
+                'where',
+                $wpdb->prepare(
+                    " AND {$id_field_name_safe} = %s",
+                    $model->get_id()
+                )
+            );

Ensure that $id_field_name is sanitized using esc_sql() to prevent SQL injection, and use %s as the placeholder for the value.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$wpdb->prepare(
" AND {$id_field_name} = $format",
$model->get_id()
)
);
$id_field_name_safe = esc_sql( $id_field_name );
$this->add_sql_clause(
'where',
$wpdb->prepare(
" AND {$id_field_name_safe} = %s",
$model->get_id()
)
);
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 78-78:
Use placeholders and $wpdb->prepare(); found interpolated variable {$id_field_name} at " AND {$id_field_name} = $format"


[failure] 78-78:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at " AND {$id_field_name} = $format"


[warning] 78-78:
Replacement variables found, but no valid placeholders found in the query.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (2)
docs/data-store.md (2)

185-192: Improve documentation clarity and completeness.

The case study section needs improvement:

  1. The word "perticulars" is misspelled (should be "particulars")
  2. The explanation could be clearer about the purpose of overriding these methods

Additionally, the documentation would benefit from new sections covering:

  • Error handling strategies
  • Data validation approaches
  • WordPress/WooCommerce hook integration

Would you like me to help draft these additional documentation sections?

🧰 Tools
🪛 LanguageTool

[uncategorized] ~188-~188: You might be missing the article “the” here.
Context: ...ta Store. perticulars field name in database is typo but I don't want to expose pu...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~190-~190: You might be missing the article “the” here.
Context: ... by overriding the following methods in Data Store. - `protected function map_model_...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~191-~191: Loose punctuation mark.
Context: ..._to_db_data( BaseModel &$model ): array`: Prepare data for saving a BaseModel to ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~192-~192: Loose punctuation mark.
Context: ...e database. - map_db_raw_to_model_data: Maps database raw data to model data.

(UNLIKELY_OPENING_PUNCTUATION)


105-106: Fix documentation formatting and grammar issues.

Several text improvements are needed:

  1. Remove double dots after "format" on line 106
  2. Add missing "the" articles:
    • Line 153: "and the format is"
    • Line 188: "field name in the database"
    • Line 190: "methods in the Data Store"
  3. Ensure consistent formatting in bullet points

Also applies to: 153-153, 188-188, 190-190

🧰 Tools
🪛 LanguageTool

[uncategorized] ~105-~105: Loose punctuation mark.
Context: ...e following methods: - get_table_name: Defines the table name in the database....

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~106-~106: Loose punctuation mark.
Context: ...the database. - get_fields_with_format: Returns the fields with format as an ar...

(UNLIKELY_OPENING_PUNCTUATION)


[typographical] ~106-~106: Two consecutive dots
Context: ...he db field name and value is the format.. Sample implementation: ```php class D...

(DOUBLE_PUNCTUATION)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 73b289d and c8b207c.

📒 Files selected for processing (2)
  • docs/data-store.md (1 hunks)
  • includes/Models/DataStore/BaseDataStore.php (1 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/data-store.md

[uncategorized] ~20-~20: Loose punctuation mark.
Context: ..., such as product. - protected $data: Holds the default data for the object. ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~105-~105: Loose punctuation mark.
Context: ...e following methods: - get_table_name: Defines the table name in the database....

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~106-~106: Loose punctuation mark.
Context: ...the database. - get_fields_with_format: Returns the fields with format as an ar...

(UNLIKELY_OPENING_PUNCTUATION)


[typographical] ~106-~106: Two consecutive dots
Context: ...he db field name and value is the format.. Sample implementation: ```php class D...

(DOUBLE_PUNCTUATION)


[uncategorized] ~153-~153: You might be missing the article “the” here.
Context: ...efault, the ID field is set to id and format is %d. ## Uses of Models ##...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[style] ~165-~165: ‘New Record’ might be wordy. Consider a shorter alternative.
Context: ... ## Uses of Models ### Create a New Record ```php $department = new Department(); ...

(EN_WORDINESS_PREMIUM_NEW_RECORD)


[uncategorized] ~188-~188: You might be missing the article “the” here.
Context: ...ta Store. perticulars field name in database is typo but I don't want to expose pu...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~190-~190: You might be missing the article “the” here.
Context: ... by overriding the following methods in Data Store. - `protected function map_model_...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~191-~191: Loose punctuation mark.
Context: ..._to_db_data( BaseModel &$model ): array`: Prepare data for saving a BaseModel to ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~192-~192: Loose punctuation mark.
Context: ...e database. - map_db_raw_to_model_data: Maps database raw data to model data.

(UNLIKELY_OPENING_PUNCTUATION)

🪛 GitHub Check: Run PHPCS inspection
includes/Models/DataStore/BaseDataStore.php

[failure] 77-77:
Use placeholders and $wpdb->prepare(); found interpolated variable {$id_field_name} at " AND {$id_field_name} = $format"


[failure] 77-77:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at " AND {$id_field_name} = $format"


[warning] 77-77:
Replacement variables found, but no valid placeholders found in the query.


[warning] 83-83:
Use placeholders and $wpdb->prepare(); found $this


[warning] 83-83:
Use placeholders and $wpdb->prepare(); found get_query_statement


[failure] 153-153:
Use placeholders and $wpdb->prepare(); found interpolated variable {$table_name} at "DELETE FROM {$table_name}


[failure] 154-154:
Use placeholders and $wpdb->prepare(); found interpolated variable $id_field_name at WHERE $id_field_name = $format"


[failure] 154-154:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at WHERE $id_field_name = $format"


[warning] 154-154:
Replacement variables found, but no valid placeholders found in the query.

🔇 Additional comments (7)
docs/data-store.md (2)

9-13: Well-designed architecture following industry best practices!

The architecture demonstrates excellent separation of concerns by abstracting the data layer through Data Stores, following WooCommerce's proven patterns. This design:

  • Reduces coupling between business logic and data persistence
  • Makes the code more maintainable and testable
  • Facilitates future modifications without affecting the entire codebase

1-192: Verify integration with WordPress/WooCommerce architecture.

The documentation presents a solid foundation for the data store implementation. Let's verify the integration points with WordPress/WooCommerce architecture.

✅ Verification successful

Based on the shell script results, I can now provide a comprehensive verification of the architectural concerns mentioned in the review comment:

Documentation accurately reflects integration with WordPress/WooCommerce architecture

The codebase shows proper integration with WordPress and WooCommerce architectures through:

  1. WordPress Integration:
  • Extensive use of WordPress action/filter hooks following naming conventions (dokan_*)
  • Proper database table creation using dbDelta()
  • Integration with WordPress admin, user roles and capabilities
  1. WooCommerce Integration:
  • Proper extension of WooCommerce data stores and interfaces (WC_Data, WC_Data_Store)
  • Following WooCommerce patterns for product, order and customer data management
  • Integration with WooCommerce hooks and filters
  1. Database Design:
  • Clean table creation with proper schema definitions
  • Tables follow WordPress naming conventions with prefix
  • Proper data types and constraints defined

The documentation accurately describes the data model and store implementation, matching the actual code implementation. The examples provided reflect real usage patterns found throughout the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify integration points with WordPress/WooCommerce

# Check for WordPress action/filter hooks in the implementation
echo "Checking for WordPress hooks integration..."
rg -A 2 "do_action|apply_filters" --type php

# Check for WooCommerce data store integration
echo "Checking for WooCommerce data store integration..."
rg -A 2 "WC_Data|WC_Object_Data_Store_Interface" --type php

# Check for proper database table creation
echo "Checking for database table creation..."
rg -A 5 "dbDelta|CREATE TABLE" --type php

Length of output: 213019

🧰 Tools
🪛 LanguageTool

[uncategorized] ~20-~20: Loose punctuation mark.
Context: ..., such as product. - protected $data: Holds the default data for the object. ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~105-~105: Loose punctuation mark.
Context: ...e following methods: - get_table_name: Defines the table name in the database....

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~106-~106: Loose punctuation mark.
Context: ...the database. - get_fields_with_format: Returns the fields with format as an ar...

(UNLIKELY_OPENING_PUNCTUATION)


[typographical] ~106-~106: Two consecutive dots
Context: ...he db field name and value is the format.. Sample implementation: ```php class D...

(DOUBLE_PUNCTUATION)


[uncategorized] ~153-~153: You might be missing the article “the” here.
Context: ...efault, the ID field is set to id and format is %d. ## Uses of Models ##...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[style] ~165-~165: ‘New Record’ might be wordy. Consider a shorter alternative.
Context: ... ## Uses of Models ### Create a New Record ```php $department = new Department(); ...

(EN_WORDINESS_PREMIUM_NEW_RECORD)


[uncategorized] ~188-~188: You might be missing the article “the” here.
Context: ...ta Store. perticulars field name in database is typo but I don't want to expose pu...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~190-~190: You might be missing the article “the” here.
Context: ... by overriding the following methods in Data Store. - `protected function map_model_...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~191-~191: Loose punctuation mark.
Context: ..._to_db_data( BaseModel &$model ): array`: Prepare data for saving a BaseModel to ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~192-~192: Loose punctuation mark.
Context: ...e database. - map_db_raw_to_model_data: Maps database raw data to model data.

(UNLIKELY_OPENING_PUNCTUATION)

includes/Models/DataStore/BaseDataStore.php (5)

6-6: Remove unused import

The Automattic\WooCommerce\Pinterest\API\Base import appears to be unused in this class and should be removed.


32-32: Remove unnecessary passing of objects by reference

Passing BaseModel objects by reference is unnecessary since objects are already passed by reference in PHP. The ampersand & can be removed to avoid confusion.

Also applies to: 57-57, 103-103, 130-130


62-62: Avoid using esc_html() in exception messages

The esc_html() function is intended for escaping output in HTML contexts, not for internal exception messages. Using it here is unnecessary.

Also applies to: 89-89


227-227: Add property existence check in map_db_raw_to_model_data()

Accessing object properties without checking their existence could lead to PHP notices or errors. It's safer to check if the property exists before accessing it.


281-283: Ensure compatibility with PHP versions when using str_starts_with()

The function str_starts_with() is available from PHP 8.0 onwards. If the project needs to support earlier PHP versions, using this function may cause compatibility issues.

Comment on lines +111 to +128
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);
}
}
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix implementation issues in DepartmentStore class.

The DepartmentStore implementation has the following issues:

  1. The get_date_updated method appears to be incorrectly placed in the store class instead of the model class
  2. Missing return type hints for better type safety

Apply these changes:

 class DepartmentStore extends \WeDevs\Dokan\Models\DataStore\BaseDataStore {
-    public function get_table_name(): string {
+    public function get_table_name(): string {
         return 'dokan_department';
     }

-    public function get_fields_with_format(): array {
+    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);
-    }
 }

Committable suggestion was skipped due to low confidence.

Comment on lines 153 to 157
"DELETE FROM {$table_name}
WHERE $id_field_name = $format",
$id
)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential SQL injection vulnerability in delete_by_id() method

Interpolating variables directly into the SQL query can lead to SQL injection vulnerabilities. Specifically, using {$table_name}, $id_field_name, and $format in the query string is unsafe.

Refactor the code to use $wpdb->delete() for safer query execution:

-        $result = $wpdb->query(
-            $wpdb->prepare(
-                "DELETE FROM {$table_name}
-                WHERE $id_field_name = $format",
-                $id
-            )
-        );
+        $result = $wpdb->delete(
+            $table_name,
+            array( $id_field_name => $id ),
+            array( $format )
+        );

This approach ensures that the table name and where clause are handled safely, preventing SQL injection.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"DELETE FROM {$table_name}
WHERE $id_field_name = $format",
$id
)
);
$result = $wpdb->delete(
$table_name,
array( $id_field_name => $id ),
array( $format )
);
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 153-153:
Use placeholders and $wpdb->prepare(); found interpolated variable {$table_name} at "DELETE FROM {$table_name}


[failure] 154-154:
Use placeholders and $wpdb->prepare(); found interpolated variable $id_field_name at WHERE $id_field_name = $format"


[failure] 154-154:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at WHERE $id_field_name = $format"


[warning] 154-154:
Replacement variables found, but no valid placeholders found in the query.

Comment on lines +77 to +80
" AND {$id_field_name} = $format",
$model->get_id()
)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential SQL injection vulnerability in read() method

Interpolating variables directly into the SQL query can lead to SQL injection vulnerabilities. Specifically, using {$id_field_name} and $format in the query string is unsafe.

Apply this diff to properly prepare the SQL statement:

            $this->add_sql_clause(
                'where',
-               $wpdb->prepare(
-                   " AND {$id_field_name} = $format",
-                   $model->get_id()
-               )
+               $wpdb->prepare(
+                   " AND " . esc_sql( $id_field_name ) . " = %s",
+                   $model->get_id()
+               )
            );

Ensure that $id_field_name is sanitized or comes from a trusted source.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
" AND {$id_field_name} = $format",
$model->get_id()
)
);
$wpdb->prepare(
" AND " . esc_sql( $id_field_name ) . " = %s",
$model->get_id()
)
);
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 77-77:
Use placeholders and $wpdb->prepare(); found interpolated variable {$id_field_name} at " AND {$id_field_name} = $format"


[failure] 77-77:
Use placeholders and $wpdb->prepare(); found interpolated variable $format at " AND {$id_field_name} = $format"


[warning] 77-77:
Replacement variables found, but no valid placeholders found in the query.

* Add delete_by method

* Add update_by to base store

* Add general update query method in Data Sotre

* Update vendor balance Model and Store to perform update operations

* Refactor dokan order sync for vendor balance

* Add database mission assertion method for test case

* Add read_meta method to BaseStore

* Add method to get vendor total balance

* Fetch vendor balance  with the method of VendorBalance Method
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🧹 Outside diff range and nitpick comments (15)
includes/Models/DataStore/VendorBalanceStore.php (3)

12-12: Replace "DOKAN_SINCE" placeholder with actual version number.

The @since tag contains a placeholder that needs to be replaced with the actual version number where this class was introduced.


19-31: Consider using more descriptive field names.

The abbreviated field names (trn_id, trn_type, trn_date) could be more descriptive. Consider using full words for better readability and maintainability:

 protected function get_fields_with_format(): array {
     return [
         'vendor_id' => '%d',
-        'trn_id' => '%d',
-        'trn_type' => '%d',
+        'transaction_id' => '%d',
+        'transaction_type' => '%d',
         'particulars' => '%s',
         'debit' => '%f',
         'credit' => '%f',
         'status' => '%s',
-        'trn_date' => '%s',
+        'transaction_date' => '%s',
         'balance_date' => '%s',
     ];
 }

64-67: Extract order status handling into a dedicated method.

The order status handling logic could be moved to a separate method for better reusability and clarity:

private function get_earning_statuses(int $vendor_id, DateTimeImmutable $balance_date): array {
    $earning_status = dokan_withdraw_get_active_order_status();
    $earning_status[] = 'approved';
    return apply_filters('dokan_vendor_total_balance_order_status', $earning_status, $vendor_id, $balance_date);
}
includes/Models/BaseModel.php (3)

14-14: Remove commented code.

The commented wc_get_product() appears to be a leftover debug or reference note.

-		// wc_get_product()

15-17: Improve error handling for missing data store.

The early return when data_store is null could be more explicit about why it's failing. Consider throwing an exception or logging a warning.

 		if ( ! $this->data_store ) {
+			wc_doing_it_wrong( __FUNCTION__, __( 'Data store is required for this operation.', 'dokan' ), '3.0.0' );
 			return $this->get_id();
 		}

50-73: Consider adding return type hint and exception handling.

The delete method could benefit from:

  1. Adding return type hint for better type safety
  2. Adding try-catch block for data store operations
-	public function delete( $force_delete = false ) {
+	public function delete( $force_delete = false ): bool {
 		$check = apply_filters( "dokan_pre_delete_{$this->object_type}", null, $this, $force_delete );
 
 		if ( null !== $check ) {
 			return $check;
 		}
 
 		if ( $this->data_store ) {
+			try {
 				$this->data_store->delete( $this, array( 'force_delete' => $force_delete ) );
 				$this->set_id( 0 );
 				return true;
+			} catch ( \Exception $e ) {
+				wc_doing_it_wrong( __FUNCTION__, $e->getMessage(), '3.0.0' );
+				return false;
+			}
 		}
 
 		return false;
includes/Models/DataStore/BaseDataStore.php (1)

6-6: Remove unused import

The Automattic\WooCommerce\Pinterest\API\Base import is not used in this class.

-use Automattic\WooCommerce\Pinterest\API\Base;
includes/Commission.php (2)

102-105: LGTM: Improved architecture with VendorBalance model.

The change from direct database update to using VendorBalance::update_by_transaction is a good architectural improvement that:

  • Encapsulates database operations in the model layer
  • Improves testability
  • Reduces direct database manipulation

Line range hint 279-282: Consider using deprecation for method renames.

The methods set_order_qunatity and get_order_qunatity have typos in their names. While fixing these is good, they are public methods that might be used by other code. Consider:

  1. Creating new methods with correct spelling
  2. Marking old methods as deprecated
  3. Making old methods call new methods internally

Here's the suggested approach:

+/**
+ * Set order quantity
+ *
+ * @since  NEW_VERSION
+ *
+ * @param int $number
+ *
+ * @return void
+ */
+public function set_order_quantity( $number ) {
+    $this->quantity = $number;
+}

/**
 * Set order quantity
 *
 * @since  2.9.21
 * @deprecated NEW_VERSION Use set_order_quantity() instead
 *
 * @param int $number
 *
 * @return void
 */
public function set_order_qunatity( $number ) {
-    $this->quantity = $number;
+    _deprecated_function( __METHOD__, 'NEW_VERSION', 'set_order_quantity' );
+    $this->set_order_quantity( $number );
}

+/**
+ * Get order quantity
+ *
+ * @since  NEW_VERSION
+ *
+ * @return int
+ */
+public function get_order_quantity() {
+    return $this->quantity;
+}

/**
 * Get order quantity
 *
 * @since  2.9.21
 * @deprecated NEW_VERSION Use get_order_quantity() instead
 *
 * @return int
 */
public function get_order_qunatity() {
-    return $this->quantity;
+    _deprecated_function( __METHOD__, 'NEW_VERSION', 'get_order_quantity' );
+    return $this->get_order_quantity();
}

Also applies to: 290-293

tests/php/src/Models/VendorBalanceModelTest.php (4)

63-88: Add PHPDoc block to test_read_method

To enhance code documentation and maintain consistency with best practices, please add a PHPDoc block for the test_read_method. This helps in understanding the purpose and usage of the test.


90-110: Add PHPDoc block to test_update_transaction_method

Please include a PHPDoc block for the test_update_transaction_method to describe its functionality and any relevant details. This improves code maintainability and readability.


112-137: Add PHPDoc block to test_dokan_sync_insert_order_function

Consider adding a PHPDoc block to the test_dokan_sync_insert_order_function method. This will provide clarity on the test's intent and usage.


139-181: Add PHPDoc block to test_get_total_balance_by_vendor_method

To maintain consistency and enhance documentation, please add a PHPDoc block for test_get_total_balance_by_vendor_method.

includes/Order/functions.php (2)

277-277: Make the 'New order' string translatable

The string 'New order' is hardcoded and not translatable. To support localization, please use the __() function to make this string translatable.

Apply this diff to make the string translatable:

-    $vendor_balance->set_particulars( 'New order' );
+    $vendor_balance->set_particulars( __( 'New order', 'dokan-lite' ) );

279-280: Cache the current datetime to ensure consistency

You're calling dokan_current_datetime() twice, which could result in slight inconsistencies due to timing differences. Consider storing the datetime in a variable and reusing it to ensure consistency.

Apply this diff to cache the datetime:

    $current_datetime = dokan_current_datetime();
    $vendor_balance->set_trn_date( $current_datetime->format( 'Y-m-d H:i:s' ) );

    $balance_datetime = clone $current_datetime;
    $balance_datetime->modify( "+ $threshold_day days" );
    $vendor_balance->set_balance_date( $balance_datetime->format( 'Y-m-d H:i:s' ) );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c8b207c and 45965a3.

📒 Files selected for processing (10)
  • includes/Commission.php (2 hunks)
  • includes/Models/BaseModel.php (1 hunks)
  • includes/Models/DataStore/BaseDataStore.php (1 hunks)
  • includes/Models/DataStore/VendorBalanceStore.php (1 hunks)
  • includes/Models/VendorBalance.php (1 hunks)
  • includes/Order/Hooks.php (2 hunks)
  • includes/Order/functions.php (2 hunks)
  • includes/Vendor/Vendor.php (2 hunks)
  • tests/php/src/CustomAssertion/DBAssertionTrait.php (1 hunks)
  • tests/php/src/Models/VendorBalanceModelTest.php (1 hunks)
🧰 Additional context used
🪛 GitHub Check: Run PHPCS inspection
includes/Models/DataStore/BaseDataStore.php

[warning] 82-82:
Replacement variables found, but no valid placeholders found in the query.


[warning] 88-88:
Use placeholders and $wpdb->prepare(); found $this


[warning] 88-88:
Use placeholders and $wpdb->prepare(); found get_query_statement


[warning] 257-257:
Replacement variables found, but no valid placeholders found in the query.

includes/Models/DataStore/VendorBalanceStore.php

[warning] 89-89:
Use placeholders and $wpdb->prepare(); found $query_statement

tests/php/src/CustomAssertion/DBAssertionTrait.php

[warning] 81-81:
json_encode() is discouraged. Use wp_json_encode() instead.

🔇 Additional comments (14)
includes/Models/DataStore/VendorBalanceStore.php (3)

37-39: LGTM!

The method is simple and follows the single responsibility principle.


1-94: Verify integration with existing vendor balance functionality.

As mentioned in past reviews, ensure this new data store implementation is properly integrated with existing vendor balance functionality. Consider:

  1. Migration plan for existing data
  2. Backwards compatibility
  3. Integration points with existing vendor balance methods
#!/bin/bash
# Check for existing vendor balance functionality
echo "Checking existing vendor balance implementations:"
rg "vendor_balance|dokan_get_vendor_balance" --type php

# Check for potential integration points
echo "Checking potential integration points:"
rg "class.*Vendor.*Balance|interface.*Vendor.*Balance" --type php
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[warning] 89-89:
Use placeholders and $wpdb->prepare(); found $query_statement


87-90: ⚠️ Potential issue

Ensure proper SQL injection protection.

While the code uses esc_sql() for arrays, the final query is not properly prepared. Consider using $wpdb->prepare() for the entire query or ensure all dynamic parts are properly escaped.

✅ Verification successful

Let me gather more information about the SQL query construction and usage.


Let me check the base class to understand the SQL query construction methods.


Let me check one final aspect about the SQL query construction.


Based on the gathered information, I can now provide the final response:

The SQL query is properly protected against SQL injection.

The code follows secure practices by:

  1. Using esc_sql() for array values in $trn_types_str and $statuses_str
  2. Using $wpdb->prepare() for dynamic values like vendor_id and date
  3. Using a structured query builder (SqlQuery class) that safely constructs the final query through add_sql_clause()
  4. The phpcs:ignore comment is justified as the final query is composed of properly escaped/prepared components

The warning can be safely ignored as the code implements proper SQL injection protection through WordPress's built-in security functions and a secure query builder pattern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for other instances of direct $wpdb->get_var usage
rg "wpdb->get_var\(" --type php

Length of output: 14558


Script:

#!/bin/bash
# Check the full context of the SQL query construction in VendorBalanceStore.php
rg -B 10 -A 5 "query_statement" includes/Models/DataStore/VendorBalanceStore.php

# Check for any SQL preparation or sanitization methods in the same file
rg "prepare|esc_sql" includes/Models/DataStore/VendorBalanceStore.php

Length of output: 1256


Script:

#!/bin/bash
# Check the BaseDataStore class implementation
rg -B 5 -A 5 "get_query_statement|add_sql_clause" --type php

# Also check for any SQL-related methods in the base class
ast-grep --pattern 'class BaseDataStore {
  $$$
}'

Length of output: 8092


Script:

#!/bin/bash
# Check the SQL query construction in BaseDataStore
rg -B 10 -A 10 "class BaseDataStore" --type php

# Also check for any SQL-related methods that handle query building
rg "get_table_name_with_prefix|clear_all_clauses" --type php

Length of output: 2727

🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[warning] 89-89:
Use placeholders and $wpdb->prepare(); found $query_statement

includes/Models/BaseModel.php (3)

1-7: LGTM! Class structure and namespace are well-defined.

The abstract class properly extends WC_Data and follows PHP best practices.


91-93: LGTM! Hook prefix method is well implemented.

The method follows WordPress naming conventions and has appropriate visibility.


7-7: Verify consistent usage of BaseModel across codebase.

Let's ensure this base model is used consistently and properly extends WC_Data functionality.

✅ Verification successful

Based on the results, I can now generate the final response:

BaseModel implementation appears consistent and well-structured

The verification shows:

  • Only one class (VendorBalance) extends BaseModel, and it properly implements the data store pattern
  • All other WC_Data usages are direct WooCommerce core implementations
  • Data store implementations across the codebase follow WooCommerce's patterns consistently
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for classes extending BaseModel and their implementation

# Find all PHP files that might extend BaseModel
echo "Checking for classes extending BaseModel..."
rg -l "extends\s+BaseModel" --type php

# Check for potential inconsistent data store usage
echo "Checking for data store implementations..."
rg "data_store\s*=" --type php -C 2

# Look for direct WC_Data usage that might need to use BaseModel instead
echo "Checking for direct WC_Data usage..."
rg "extends\s+WC_Data" --type php -C 2

Length of output: 3895

includes/Models/VendorBalance.php (1)

283-295: LGTM: Effective caching implementation.

The get_total_balance_by_vendor method:

  • Properly handles date validation and conversion
  • Implements caching effectively with vendor-specific cache keys
  • Uses appropriate cache group naming
includes/Models/DataStore/BaseDataStore.php (1)

79-85: ⚠️ Potential issue

Fix SQL injection vulnerability in read() method

The where clause is vulnerable to SQL injection as it directly interpolates variables into the query string.

         $this->add_sql_clause(
             'where',
             $wpdb->prepare(
-                " AND {$id_field_name} = $format",
+                " AND " . esc_sql($id_field_name) . " = %d",
                 $model->get_id()
             )
         );

Likely invalid or redundant comment.

🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[warning] 82-82:
Replacement variables found, but no valid placeholders found in the query.

includes/Order/Hooks.php (2)

7-7: LGTM!

The import statement is correctly placed and follows PSR-4 autoloading standards.


129-132: ⚠️ Potential issue

Add error handling for the balance update operation.

While the migration to using the VendorBalance model is a good improvement, the update operation should include error handling to ensure vendor balances are accurately maintained.

-VendorBalance::update_by_transaction(
-    $order_id,
-    VendorBalance::TRN_TYPE_DOKAN_ORDERS,
-    [ 'status' => $new_status ]
-);
+try {
+    VendorBalance::update_by_transaction(
+        $order_id,
+        VendorBalance::TRN_TYPE_DOKAN_ORDERS,
+        [ 'status' => $new_status ]
+    );
+} catch (\Exception $e) {
+    dokan_log(
+        sprintf(
+            'Failed to update vendor balance for order #%d: %s',
+            $order_id,
+            $e->getMessage()
+        )
+    );
+    // Optionally, add an order note about the failed balance update
+    $order->add_order_note(
+        sprintf(
+            __('Failed to update vendor balance: %s', 'dokan-lite'),
+            $e->getMessage()
+        )
+    );
+}

Let's verify the transaction type constant and its usage:

includes/Commission.php (1)

7-7: LGTM: Import statements are correctly added.

The new imports for VendorBalance and Vendor classes are properly added and align with their usage in the code.

Also applies to: 9-9

includes/Vendor/Vendor.php (2)

8-8: LGTM! Clean import of the new VendorBalance model.

The import follows PSR-4 autoloading standards and maintains proper ordering.


790-790: LGTM! Good refactoring to use the new VendorBalance model.

The change improves code organization by moving vendor balance logic to a dedicated model class.

Let's verify the implementation of the new method being called:

✅ Verification successful

The refactoring is well-implemented and properly tested

The verification confirms:

  • The get_total_balance_by_vendor method is properly implemented in includes/Models/VendorBalance.php
  • The method is covered by unit tests in VendorBalanceModelTest.php
  • The implementation includes proper caching mechanism for performance
  • The method signature matches the usage in the Vendor class
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of get_total_balance_by_vendor method

# Test: Check if the method exists and its implementation
ast-grep --pattern 'public static function get_total_balance_by_vendor($vendor_id, $on_date)'

Length of output: 95


Script:

#!/bin/bash
# Let's try a broader search for the VendorBalance class and its methods

# Search for VendorBalance class definition and its methods
rg -l "class VendorBalance"

# Search for get_total_balance_by_vendor method with more context
rg "get_total_balance_by_vendor" -A 5

Length of output: 2193

includes/Order/functions.php (1)

4-4: Ensure the 'VendorBalance' class is properly imported

The VendorBalance class is imported correctly, allowing its usage within this file.

Comment on lines +78 to +82
public function assertDatabaseMissing( string $table, array $data = [] ): void {
$rows_count = $this->getDatabaseCount( $table, $data );

$this->assertGreaterThanOrEqual( 0, $rows_count, "{$rows_count} rows found in `$table` for given data " . json_encode( $data ) );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: Fix assertion logic and add documentation

The current implementation has several issues:

  1. The assertion assertGreaterThanOrEqual(0, $rows_count) will always pass since row counts cannot be negative. This defeats the purpose of asserting that records are missing.
  2. Missing PHPDoc block for documenting the method's purpose and parameters.
  3. Using json_encode instead of WordPress's wp_json_encode.

Apply this diff to fix the issues:

+    /**
+     * Assert that a table does not contain any rows matching the specified criteria.
+     *
+     * @param string $table The name of the table (without the prefix).
+     * @param array  $data  An associative array of field-value pairs to match.
+     * @return void
+     */
     public function assertDatabaseMissing( string $table, array $data = [] ): void {
         $rows_count = $this->getDatabaseCount( $table, $data );
 
-        $this->assertGreaterThanOrEqual( 0, $rows_count, "{$rows_count} rows found in `$table` for given data " . json_encode( $data ) );
+        $this->assertEquals( 0, $rows_count, "Expected `$table` to not have any matching rows, but found {$rows_count} for data " . wp_json_encode( $data ) );
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function assertDatabaseMissing( string $table, array $data = [] ): void {
$rows_count = $this->getDatabaseCount( $table, $data );
$this->assertGreaterThanOrEqual( 0, $rows_count, "{$rows_count} rows found in `$table` for given data " . json_encode( $data ) );
}
/**
* Assert that a table does not contain any rows matching the specified criteria.
*
* @param string $table The name of the table (without the prefix).
* @param array $data An associative array of field-value pairs to match.
* @return void
*/
public function assertDatabaseMissing( string $table, array $data = [] ): void {
$rows_count = $this->getDatabaseCount( $table, $data );
$this->assertEquals( 0, $rows_count, "Expected `$table` to not have any matching rows, but found {$rows_count} for data " . wp_json_encode( $data ) );
}
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[warning] 81-81:
json_encode() is discouraged. Use wp_json_encode() instead.


💡 Codebase verification

Additional test coverage needed for assertDatabaseMissing

The assertion method is currently only used in one test case (VendorBalanceModelTest::test_dokan_sync_insert_order_function), which tests a negative scenario. There's no dedicated test suite for the assertion trait itself, and the current usage doesn't cover all edge cases (e.g., empty data array, non-existent table, multiple matching conditions).

Recommended test scenarios to add:

  • Test with empty data array
  • Test with multiple conditions in data array
  • Test with non-existent table
  • Test with data that exists (should fail)
🔗 Analysis chain

Verify test coverage for the new assertion method

Since this is a testing utility, we should ensure it's properly tested with both positive and negative scenarios.

Let's check if there are corresponding test cases:


Let me check the actual test implementation to better understand the test coverage.


Let me check if there are any other test files that might be testing this assertion trait directly.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for test cases using assertDatabaseMissing
rg "assertDatabaseMissing" tests/

Length of output: 255


Script:

#!/bin/bash
# Get the context of the test case in VendorBalanceModelTest.php
rg -B 5 -A 5 "assertDatabaseMissing" tests/php/src/Models/VendorBalanceModelTest.php

Length of output: 511


Script:

#!/bin/bash
# Look for test files related to DBAssertionTrait
fd "DBAssertion.*Test\.php" tests/
# Also check if there's a dedicated test directory for assertions
fd "Test\.php" tests/php/src/CustomAssertion/

Length of output: 84

🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[warning] 81-81:
json_encode() is discouraged. Use wp_json_encode() instead.

Comment on lines 59 to 93
public function get_total_balance( $vendor_id, DateTimeImmutable $balance_date ): float {
global $wpdb;

$on_date = $balance_date->format( 'Y-m-d' );

$earning_status = dokan_withdraw_get_active_order_status();
$earning_status[] = 'approved';
$earning_status = apply_filters( 'dokan_vendor_total_balance_order_status', $earning_status, $vendor_id, $balance_date );
$statuses_str = "'" . implode( "', '", esc_sql( $earning_status ) ) . "'";

$trn_types = apply_filters(
'dokan_vendor_total_balance_trn_types', [
VendorBalance::TRN_TYPE_DOKAN_ORDERS,
VendorBalance::TRN_TYPE_DOKAN_REFUND,
], $vendor_id, $balance_date
);

$trn_types_str = "'" . implode( "', '", esc_sql( $trn_types ) ) . "'";

$this->add_sql_clause( 'select', 'SUM(debit) - SUM(credit) AS earnings' );
$this->add_sql_clause( 'from', $this->get_table_name_with_prefix() );
$this->add_sql_clause( 'where', $wpdb->prepare( ' AND vendor_id = %d', $vendor_id ) );
$this->add_sql_clause( 'where', $wpdb->prepare( ' AND DATE(balance_date) <= %s ', $on_date ) );
$this->add_sql_clause( 'where', " AND trn_type IN ($trn_types_str)" );
$this->add_sql_clause( 'where', " AND status IN ($statuses_str)" );

$query_statement = $this->get_query_statement();

// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$earnings = $wpdb->get_var(
$query_statement
);

return floatval( $earnings );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider breaking down the complex method into smaller, focused functions.

The get_total_balance method handles multiple responsibilities. Consider extracting the following into separate methods:

  1. Status handling
  2. Transaction type handling
  3. Query building

This would improve readability and maintainability.

🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[warning] 89-89:
Use placeholders and $wpdb->prepare(); found $query_statement

Comment on lines +101 to +103
public function get_meta_data() {
return apply_filters( $this->get_hook_prefix() . 'meta_data', array() );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Review meta data implementation.

The current implementation seems incomplete:

  1. It returns an empty array by default without actually retrieving meta data
  2. It doesn't utilize WC_Data's meta data functionality

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function get_meta_data() {
return apply_filters( $this->get_hook_prefix() . 'meta_data', array() );
}
public function get_meta_data() {
$meta_data = parent::get_meta_data();
return apply_filters( $this->get_hook_prefix() . 'meta_data', $meta_data );
}

Comment on lines +81 to +84
public static function delete_by( array $data ) {
$object = new static();
return $object->data_store->delete_by( $data );
}
Copy link
Contributor

Choose a reason for hiding this comment

The 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:

  1. Return type hint
  2. Error handling for data store operations
  3. Documentation for possible exceptions
+	/**
+	 * 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static function delete_by( array $data ) {
$object = new static();
return $object->data_store->delete_by( $data );
}
/**
* 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 ): 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;
}
}

Comment on lines +191 to +193
public function set_debit( float $amount ) {
$this->set_prop( 'debit', $amount );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation for monetary values.

The setter methods for monetary values should validate that the amounts are non-negative:

 public function set_debit( float $amount ) {
+    if ( $amount < 0 ) {
+        throw new \InvalidArgumentException( 'Debit amount must be non-negative.' );
+    }
     $this->set_prop( 'debit', $amount );
 }

 public function set_credit( float $amount ) {
+    if ( $amount < 0 ) {
+        throw new \InvalidArgumentException( 'Credit amount must be non-negative.' );
+    }
     $this->set_prop( 'credit', $amount );
 }

Also applies to: 211-213

Comment on lines +37 to +50
public function create( BaseModel &$model ) {
$data = $this->map_model_to_db_data( $model );

$inserted_id = $this->insert( $data );

if ( $inserted_id ) {
$model->set_id( $inserted_id );
$model->apply_changes();
}

do_action( $this->get_hook_prefix() . 'created', $inserted_id, $data );

return $inserted_id;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve error handling in database operations

The create(), update(), and insert() methods should handle database errors more gracefully:

  1. Check for false/null return values from $wpdb operations
  2. Throw specific exceptions with meaningful messages
  3. Log errors for debugging

Example improvement for the create() method:

     public function create( BaseModel &$model ) {
+        try {
             $data = $this->map_model_to_db_data( $model );
 
             $inserted_id = $this->insert( $data );
 
-            if ( $inserted_id ) {
+            if ( $inserted_id === 0 ) {
+                throw new Exception( sprintf( 
+                    __( 'Failed to create record in %s', 'dokan-lite' ),
+                    $this->get_table_name()
+                ) );
+            }
                 $model->set_id( $inserted_id );
                 $model->apply_changes();
-            }
 
             do_action( $this->get_hook_prefix() . 'created', $inserted_id, $data );
 
             return $inserted_id;
+        } catch ( Exception $e ) {
+            error_log( sprintf( '[Dokan] Database error in %s: %s', __METHOD__, $e->getMessage() ) );
+            throw $e;
+        }
     }

Also applies to: 108-127, 274-292

Comment on lines +175 to +178
$result = $wpdb->query(
"DELETE FROM {$table_name}
WHERE {$where_clause}"
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix SQL injection vulnerability in delete_by() method

The DELETE query is vulnerable to SQL injection as it directly interpolates the table name into the query.

-        $result = $wpdb->query(
-            "DELETE FROM {$table_name}
-                WHERE {$where_clause}"
-        );
+        $result = $wpdb->query(
+            $wpdb->prepare(
+                "DELETE FROM " . esc_sql($table_name) . " WHERE " . $where_clause
+            )
+        );

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +241 to +265
protected function prepare_where_clause( array $data ): string {
global $wpdb;

$where = [ '1=1' ];

$field_format = $this->get_fields_with_format();

// Add ID field format for the WHERE clause.
$field_format[ $this->get_id_field_name() ] = $this->get_id_field_format();

foreach ( $data as $key => $value ) {
if ( is_array( $value ) ) {
// Fill format array based on the number of values and specified format for the key.
$placeholders = implode( ',', array_fill( 0, count( $value ), '%s' ) );

// Generate the WHERE clause securely using $wpdb->prepare() with placeholders.
$where[] = $wpdb->prepare( "{$key} IN ({$placeholders})", ...$value );
} else {
$format = $field_format[ $key ];
$where[] = $wpdb->prepare( "{$key} = {$format}", $value );
}
}

return implode( ' AND ', $where );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve security in prepare_where_clause() method

The method needs additional security measures:

  1. Validate column names against a whitelist
  2. Use proper escaping for column names
     protected function prepare_where_clause( array $data ): string {
         global $wpdb;
 
         $where = [ '1=1' ];
 
         $field_format = $this->get_fields_with_format();
+        $allowed_columns = array_keys($field_format);
 
         // Add ID field format for the WHERE clause.
         $field_format[ $this->get_id_field_name() ] = $this->get_id_field_format();
+        $allowed_columns[] = $this->get_id_field_name();
 
         foreach ( $data as $key => $value ) {
+            if (!in_array($key, $allowed_columns, true)) {
+                continue;
+            }
+            $escaped_key = esc_sql($key);
             if ( is_array( $value ) ) {
                 $placeholders = implode( ',', array_fill( 0, count( $value ), '%s' ) );
-                $where[] = $wpdb->prepare( "{$key} IN ({$placeholders})", ...$value );
+                $where[] = $wpdb->prepare( "{$escaped_key} IN ({$placeholders})", ...$value );
             } else {
                 $format = $field_format[ $key ];
-                $where[] = $wpdb->prepare( "{$key} = {$format}", $value );
+                $where[] = $wpdb->prepare( "{$escaped_key} = {$format}", $value );
             }
         }
 
         return implode( ' AND ', $where );
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
protected function prepare_where_clause( array $data ): string {
global $wpdb;
$where = [ '1=1' ];
$field_format = $this->get_fields_with_format();
// Add ID field format for the WHERE clause.
$field_format[ $this->get_id_field_name() ] = $this->get_id_field_format();
foreach ( $data as $key => $value ) {
if ( is_array( $value ) ) {
// Fill format array based on the number of values and specified format for the key.
$placeholders = implode( ',', array_fill( 0, count( $value ), '%s' ) );
// Generate the WHERE clause securely using $wpdb->prepare() with placeholders.
$where[] = $wpdb->prepare( "{$key} IN ({$placeholders})", ...$value );
} else {
$format = $field_format[ $key ];
$where[] = $wpdb->prepare( "{$key} = {$format}", $value );
}
}
return implode( ' AND ', $where );
}
protected function prepare_where_clause( array $data ): string {
global $wpdb;
$where = [ '1=1' ];
$field_format = $this->get_fields_with_format();
$allowed_columns = array_keys($field_format);
// Add ID field format for the WHERE clause.
$field_format[ $this->get_id_field_name() ] = $this->get_id_field_format();
$allowed_columns[] = $this->get_id_field_name();
foreach ( $data as $key => $value ) {
if (!in_array($key, $allowed_columns, true)) {
continue;
}
$escaped_key = esc_sql($key);
if ( is_array( $value ) ) {
$placeholders = implode( ',', array_fill( 0, count( $value ), '%s' ) );
$where[] = $wpdb->prepare( "{$escaped_key} IN ({$placeholders})", ...$value );
} else {
$format = $field_format[ $key ];
$where[] = $wpdb->prepare( "{$escaped_key} = {$format}", $value );
}
}
return implode( ' AND ', $where );
}
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[warning] 257-257:
Replacement variables found, but no valid placeholders found in the query.

Comment on lines 180 to 181
$this->assertGreaterThan( 110, $total_balance );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct the assertion to match the expected total balance

The calculation comment indicates an expected total balance of 110 (100 + 60 - 20 - 30 = 110). However, the assertion uses assertGreaterThan( 110, $total_balance ), which may not align with the expected result.

Apply this diff to correct the assertion:

-        $this->assertGreaterThan( 110, $total_balance );
+        $this->assertEquals( 110, $total_balance );

This change ensures that the test accurately verifies the total balance is exactly 110 as per the calculation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$this->assertGreaterThan( 110, $total_balance );
}
$this->assertEquals( 110, $total_balance );
}

$vendor_balance->set_debit( $net_amount );
$vendor_balance->set_trn_date( dokan_current_datetime()->format( 'Y-m-d H:i:s' ) );
$vendor_balance->set_balance_date( dokan_current_datetime()->modify( "+ $threshold_day days" )->format( 'Y-m-d H:i:s' ) );
$vendor_balance->save();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Handle potential exceptions from the save() method

The save() method may throw exceptions if the operation fails. To prevent uncaught exceptions and handle errors gracefully, consider wrapping the save() call in a try-catch block.

Apply this diff to add error handling:

-    $vendor_balance->save();
+    try {
+        $vendor_balance->save();
+    } catch ( Exception $e ) {
+        // Handle the exception, e.g., log the error or display an admin notice
+        error_log( 'Failed to save vendor balance: ' . $e->getMessage() );
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$vendor_balance->save();
try {
$vendor_balance->save();
} catch ( Exception $e ) {
// Handle the exception, e.g., log the error or display an admin notice
error_log( 'Failed to save vendor balance: ' . $e->getMessage() );
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (10)
includes/DependencyManagement/Providers/ModelServiceProvider.php (1)

7-11: Consider renaming the TAG constant.

The constant value 'ajax-service' seems misaligned with the purpose of this service provider, which is focused on model services. Consider using a more appropriate tag like 'model-service' to better reflect its responsibility.

-    public const TAG = 'ajax-service';
+    public const TAG = 'model-service';
includes/Models/DataStore/VendorBalanceStore.php (3)

9-13: Update class documentation with version information.

The @since DOKAN_SINCE placeholder needs to be replaced with the actual version number where this class was introduced.


19-31: Standardize field names for better maintainability.

Consider using more consistent and descriptive field names:

  • trn_idtransaction_id
  • trn_typetransaction_type
  • trn_datetransaction_date

This would improve code readability and maintain consistency with modern naming conventions.


88-90: Use prepared statements consistently.

The PHPCS warning about unprepared SQL is valid. While the query is built using prepared statements in the clauses, it's good practice to use $wpdb->prepare() for the final query as well.

-$earnings = $wpdb->get_var(
-    $query_statement
-);
+$earnings = $wpdb->get_var(
+    $wpdb->prepare('%s', $query_statement)
+);
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[warning] 89-89:
Use placeholders and $wpdb->prepare(); found $query_statement

includes/DependencyManagement/Providers/ServiceProvider.php (1)

61-64: Consider documenting provider initialization order.

The order of service provider initialization can be crucial. Consider adding a comment block above the provider registrations explaining any dependencies between providers and why this specific order is chosen.

Example documentation:

 public function boot(): void {
+    // Provider initialization order:
+    // 1. AdminServiceProvider: Core admin functionality
+    // 2. CommonServiceProvider: Shared services
+    // 3. FrontendServiceProvider: Frontend-specific services
+    // 4. AjaxServiceProvider: AJAX handlers
+    // 5. AnalyticsServiceProvider: Analytics tracking
+    // 6. ModelServiceProvider: Data models and persistence
     $this->getContainer()->addServiceProvider( new AdminServiceProvider() );
tests/php/src/Models/VendorBalanceModelTest.php (4)

63-88: Enhance test_read_method with error cases and documentation.

  1. Add PHPDoc block
  2. Add test cases for error scenarios:
    • Reading non-existent ID
    • Reading invalid ID (e.g., negative number)
+    /**
+     * Test reading a VendorBalance instance from database
+     *
+     * @since 3.x.x
+     * @return void
+     */
     public function test_read_method() {
         // ... existing test code ...
     }

+    /**
+     * Test reading a VendorBalance instance with invalid data
+     *
+     * @since 3.x.x
+     * @return void
+     */
+    public function test_read_method_with_invalid_data() {
+        $this->expectException(\InvalidArgumentException::class);
+        new VendorBalance(-1);
+
+        $non_existent_id = 999999;
+        $vendor_balance = new VendorBalance($non_existent_id);
+        $this->assertNull($vendor_balance->get_id());
+    }

90-110: Improve test coverage for update_transaction_method.

  1. Add PHPDoc block
  2. Verify unchanged fields remain intact
  3. Add test cases for:
    • Invalid transaction ID
    • Invalid transaction type
    • Empty update data
+    /**
+     * Test updating a VendorBalance instance by transaction
+     *
+     * @since 3.x.x
+     * @return void
+     */
     public function test_update_transaction_method() {
         $vendor_balance = new VendorBalance();
         // ... existing setup code ...
 
         $result = VendorBalance::update_by_transaction(
             1, VendorBalance::TRN_TYPE_DOKAN_ORDERS, [
                 'debit' => 200,
             ]
         );
 
         $updated_vendor_balance = new VendorBalance( $vendor_balance->get_id() );
         $this->assertEquals( 200, $updated_vendor_balance->get_debit() );
+        // Verify other fields remained unchanged
+        $this->assertEquals( 'test', $updated_vendor_balance->get_particulars() );
+        $this->assertEquals( 1, $updated_vendor_balance->get_vendor_id() );
     }

112-137: Add error cases and documentation for sync order test.

  1. Add PHPDoc block
  2. Test error scenarios:
    • Invalid order ID
    • Already synced orders
    • Failed synchronization
+    /**
+     * Test synchronization of orders with vendor balance
+     *
+     * @since 3.x.x
+     * @return void
+     */
     public function test_dokan_sync_insert_order_function() {
         // ... existing test code ...
     }

+    /**
+     * Test synchronization failures
+     *
+     * @since 3.x.x
+     * @return void
+     */
+    public function test_dokan_sync_insert_order_function_failures() {
+        // Test with invalid order ID
+        dokan_sync_insert_order(-1);
+        $this->assertDatabaseMissing('dokan_vendor_balance', ['trn_id' => -1]);
+
+        // Test with already synced order
+        $order_service = Mockery::mock('\WeDevs\Dokan\Order\Manager[is_order_already_synced]');
+        $order_service->shouldReceive('is_order_already_synced')->andReturn(true);
+        dokan()->get_container()->extend('order')->setConcrete($order_service);
+        
+        $order_id = $this->create_single_vendor_order($this->seller_id1);
+        dokan_sync_insert_order($order_id);
+        
+        // Verify no duplicate entry was created
+        $this->assertDatabaseCount('dokan_vendor_balance', 1, [
+            'trn_id' => $order_id,
+        ]);
+    }

139-180: Improve test structure and documentation.

  1. Add PHPDoc block
  2. Break down the test into smaller, focused test methods
  3. Use data providers for different scenarios
+    /**
+     * Test calculation of total earnings for a vendor
+     *
+     * @since 3.x.x
+     * @return void
+     */
     public function test_get_total_earning_by_vendor_method() {
         // ... existing test code ...
     }

+    /**
+     * Data provider for vendor earnings test cases
+     *
+     * @return array
+     */
+    public function earning_scenarios_provider() {
+        return [
+            'only_orders' => [
+                'transactions' => [
+                    ['type' => VendorBalance::TRN_TYPE_DOKAN_ORDERS, 'debit' => 100],
+                    ['type' => VendorBalance::TRN_TYPE_DOKAN_ORDERS, 'debit' => 60],
+                ],
+                'expected' => 160
+            ],
+            'orders_and_refunds' => [
+                'transactions' => [
+                    ['type' => VendorBalance::TRN_TYPE_DOKAN_ORDERS, 'debit' => 100],
+                    ['type' => VendorBalance::TRN_TYPE_DOKAN_REFUND, 'credit' => 20],
+                ],
+                'expected' => 80
+            ],
+        ];
+    }

+    /**
+     * @dataProvider earning_scenarios_provider
+     */
+    public function test_get_total_earning_by_vendor_scenarios($transactions, $expected) {
+        foreach ($transactions as $transaction) {
+            $vendor_balance = new VendorBalance();
+            $vendor_balance->set_vendor_id($this->seller_id1);
+            if (isset($transaction['debit'])) {
+                $vendor_balance->set_debit($transaction['debit']);
+            }
+            if (isset($transaction['credit'])) {
+                $vendor_balance->set_credit($transaction['credit']);
+            }
+            $vendor_balance->set_trn_type($transaction['type']);
+            $vendor_balance->save();
+        }
+
+        $total = VendorBalance::get_total_earning_by_vendor($this->seller_id1);
+        $this->assertEquals($expected, $total);
+    }
includes/Order/functions.php (1)

278-278: Improve code readability and safety

Two suggestions for improvement:

  1. Make the particulars text more descriptive by including the order ID
  2. Use DateInterval for threshold days calculation instead of string concatenation

Apply these improvements:

-    $vendor_balance->set_particulars( 'New order' );
+    $vendor_balance->set_particulars( sprintf( 'New order #%d', $order_id ) );

-    $vendor_balance->set_balance_date( dokan_current_datetime()->modify( "+ $threshold_day days" )->format( 'Y-m-d H:i:s' ) );
+    $vendor_balance->set_balance_date( dokan_current_datetime()->add( new DateInterval( "P{$threshold_day}D" ) )->format( 'Y-m-d H:i:s' ) );

Also applies to: 281-281

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 45965a3 and 774843d.

📒 Files selected for processing (9)
  • includes/Commission.php (2 hunks)
  • includes/DependencyManagement/Providers/ModelServiceProvider.php (1 hunks)
  • includes/DependencyManagement/Providers/ServiceProvider.php (1 hunks)
  • includes/Models/DataStore/VendorBalanceStore.php (1 hunks)
  • includes/Models/VendorBalance.php (1 hunks)
  • includes/Order/Hooks.php (2 hunks)
  • includes/Order/functions.php (2 hunks)
  • includes/Vendor/Vendor.php (15 hunks)
  • tests/php/src/Models/VendorBalanceModelTest.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • includes/Commission.php
  • includes/Order/Hooks.php
🧰 Additional context used
🪛 GitHub Check: Run PHPCS inspection
includes/Models/DataStore/VendorBalanceStore.php

[warning] 89-89:
Use placeholders and $wpdb->prepare(); found $query_statement

includes/Vendor/Vendor.php

[failure] 904-904:
A function call to _n() with texts containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.


[failure] 905-905:
A function call to __() with texts containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.

🔇 Additional comments (19)
includes/DependencyManagement/Providers/ModelServiceProvider.php (3)

1-6: LGTM! Clean namespace and import structure.

The file follows PSR-4 autoloading standards with proper namespace declaration and relevant import.


26-28: LGTM! Well-implemented service check.

The provides() method is well-implemented with:

  • Proper type hinting
  • Strict comparison in in_array
  • Clear documentation

13-16: Consider using imports for better readability.

The services array uses fully qualified class names. Consider importing these classes at the top of the file for better readability and maintainability.

+ use WeDevs\Dokan\Models\VendorBalance;
+ use WeDevs\Dokan\Models\DataStore\VendorBalanceStore;
+
  protected $services = [
-     \WeDevs\Dokan\Models\VendorBalance::class,
-     \WeDevs\Dokan\Models\DataStore\VendorBalanceStore::class,
+     VendorBalance::class,
+     VendorBalanceStore::class,
  ];

Additionally, let's verify the existence of these classes:

✅ Verification successful

Let me try a different search strategy to verify the existence of these classes.


Using imports would improve code readability

Both classes exist in the codebase:

  • VendorBalance is defined in includes/Models/VendorBalance.php
  • VendorBalanceStore is defined in includes/Models/DataStore/VendorBalanceStore.php

The suggestion to use imports is valid and would improve code maintainability without introducing any risks.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence of the model classes

# Search for VendorBalance class definition
echo "Searching for VendorBalance class..."
ast-grep --pattern 'class VendorBalance'

# Search for VendorBalanceStore class definition
echo "Searching for VendorBalanceStore class..."
ast-grep --pattern 'class VendorBalanceStore'

Length of output: 266


Script:

#!/bin/bash
# Search for files containing these class names using ripgrep
echo "Searching for VendorBalance files..."
rg -l "VendorBalance" --type php

echo -e "\nSearching for VendorBalanceStore files..."
rg -l "VendorBalanceStore" --type php

# Get content of any matching files to verify class definitions
echo -e "\nChecking class definitions in matching files..."
rg "class\s+(VendorBalance|VendorBalanceStore)" --type php -A 1

Length of output: 1447

includes/Models/DataStore/VendorBalanceStore.php (1)

37-39: LGTM!

The table name getter is properly implemented.

includes/DependencyManagement/Providers/ServiceProvider.php (1)

64-64: LGTM! Verify ModelServiceProvider implementation.

The addition follows the existing pattern for service provider registration. However, let's verify the ModelServiceProvider implementation.

✅ Verification successful

The verification results show that:

  1. ModelServiceProvider exists at includes/DependencyManagement/Providers/ModelServiceProvider.php
  2. It registers VendorBalance and VendorBalanceStore classes
  3. The VendorBalance model and its store are properly implemented with:
    • Proper class inheritance (extends BaseModel)
    • Complete test coverage in tests/php/src/Models/VendorBalanceModelTest.php
    • Integration with other components (Commission, Order, Vendor)
    • Well-defined transaction types and data store operations

Let me generate one final verification to check if ModelServiceProvider extends the correct base class:


ModelServiceProvider implementation is correct and well-structured

The ModelServiceProvider:

  • Properly extends BaseServiceProvider
  • Correctly registers VendorBalance and VendorBalanceStore services
  • Implements required provider methods (provides, register)
  • Follows the established service provider pattern
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify ModelServiceProvider exists and follows the expected structure

# Check if ModelServiceProvider exists
fd --type f "ModelServiceProvider.php"

# Check if it extends the base provider class
ast-grep --pattern 'class ModelServiceProvider extends $_ {
  $$$
}'

# Look for registration of VendorBalance related services
rg -A 5 "VendorBalance" --type php

Length of output: 18880


Script:

#!/bin/bash
# Check ModelServiceProvider implementation
cat includes/DependencyManagement/Providers/ModelServiceProvider.php

Length of output: 1088

tests/php/src/Models/VendorBalanceModelTest.php (3)

15-32: Previous review comment is still applicable.


34-50: Previous review comment is still applicable.


52-61: Previous review comment is still applicable.

includes/Models/VendorBalance.php (4)

49-57: LGTM! Well-structured constructor implementation.

The constructor follows good practices:

  • Uses dependency injection for the data store
  • Properly initializes the model with optional ID
  • Follows WordPress coding standards

288-300: LGTM! Efficient caching implementation.

The method implements caching effectively:

  • Uses vendor-specific cache keys
  • Properly handles date formatting
  • Falls back to data store when cache miss occurs

8-11: 🛠️ Refactor suggestion

Add transaction type validation using class constants.

The transaction type constants are well-defined, but there's no validation in the set_trn_type method to ensure only valid types are used.

 public function set_trn_type( string $type ) {
+    $valid_types = [
+        self::TRN_TYPE_DOKAN_ORDERS,
+        self::TRN_TYPE_DOKAN_WITHDRAW,
+        self::TRN_TYPE_DOKAN_REFUND,
+    ];
+    if ( ! in_array( $type, $valid_types, true ) ) {
+        throw new \InvalidArgumentException( 'Invalid transaction type.' );
+    }
     $this->set_prop( 'trn_type', $type );
 }

Likely invalid or redundant comment.


71-81: 🛠️ Refactor suggestion

Add input validation for transaction updates.

The method should validate its input parameters to ensure data integrity.

 public static function update_by_transaction( int $trn_id, string $trn_type, array $data ): int {
+    if ( $trn_id <= 0 ) {
+        throw new \InvalidArgumentException( 'Transaction ID must be positive.' );
+    }
+
+    if ( empty( $data ) ) {
+        throw new \InvalidArgumentException( 'Data array cannot be empty.' );
+    }
+
     $model = new static();
     // ... rest of the method
 }

Likely invalid or redundant comment.

includes/Order/functions.php (2)

4-4: LGTM!

The import statement follows PSR-4 autoloading standards and is correctly placed with other imports.


273-282: ⚠️ Potential issue

Add error handling for the save operation

The save operation could fail and throw exceptions. As suggested in the previous review, we should handle potential errors gracefully.

Apply this diff to add error handling:

-    $vendor_balance->save();
+    try {
+        $vendor_balance->save();
+    } catch ( Exception $e ) {
+        dokan_log( sprintf( 'Failed to save vendor balance for order #%d: %s', $order_id, $e->getMessage() ) );
+        return;
+    }
includes/Vendor/Vendor.php (5)

227-230: LGTM! Improved code robustness.

The changes enhance code reliability by:

  1. Explicitly defining the payment array structure
  2. Adding defensive programming to prevent PHP notices when vendor address is empty

Also applies to: 255-255


430-433: LGTM! Enhanced code readability.

The location handling is improved with explicit default values and clearer array structure.


870-886: LGTM! Improved rating calculation structure.

The changes enhance code quality through:

  1. Better SQL query formatting
  2. More explicit rating value calculation

1438-1443: LGTM! Enhanced payment property handling.

The changes improve code reliability through better initialization and change tracking for payment properties.


1462-1467: LGTM! Consistent store time property handling.

The changes maintain consistency with other property handlers through proper initialization and change tracking.

Comment on lines +33 to +37
public function register(): void {
foreach ( $this->services as $service ) {
$this->getContainer()->add( $service, $service );
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding error handling and service validation.

The register() method could be more robust with:

  1. Error handling for invalid service classes
  2. Verification if service is already registered
  3. More descriptive documentation including @throws annotation
     /**
      * 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function register(): void {
foreach ( $this->services as $service ) {
$this->getContainer()->add( $service, $service );
}
}
/**
* 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 );
}
}

$earning_status = dokan_withdraw_get_active_order_status();
$earning_status[] = 'approved';
$earning_status = apply_filters( 'dokan_vendor_total_balance_order_status', $earning_status, $vendor_id, $balance_date );
$statuses_str = "'" . implode( "', '", esc_sql( $earning_status ) ) . "'";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix SQL injection vulnerability in status and transaction type handling.

The current implementation using implode() with esc_sql() is vulnerable to SQL injection. Use $wpdb->prepare() with placeholders instead.

Here's how to fix it:

-$statuses_str = "'" . implode( "', '", esc_sql( $earning_status ) ) . "'";
+$placeholders = array_fill(0, count($earning_status), '%s');
+$statuses_str = $wpdb->prepare(implode(',', $placeholders), $earning_status);

-$trn_types_str = "'" . implode( "', '", esc_sql( $trn_types ) ) . "'";
+$placeholders = array_fill(0, count($trn_types), '%s');
+$trn_types_str = $wpdb->prepare(implode(',', $placeholders), $trn_types);

Also applies to: 76-76

Comment on lines +59 to +93
public function get_total_earning_by_vendor( $vendor_id, DateTimeImmutable $balance_date ): float {
global $wpdb;

$on_date = $balance_date->format( 'Y-m-d' );

$earning_status = dokan_withdraw_get_active_order_status();
$earning_status[] = 'approved';
$earning_status = apply_filters( 'dokan_vendor_total_balance_order_status', $earning_status, $vendor_id, $balance_date );
$statuses_str = "'" . implode( "', '", esc_sql( $earning_status ) ) . "'";

$trn_types = apply_filters(
'dokan_vendor_total_balance_trn_types', [
VendorBalance::TRN_TYPE_DOKAN_ORDERS,
VendorBalance::TRN_TYPE_DOKAN_REFUND,
], $vendor_id, $balance_date
);

$trn_types_str = "'" . implode( "', '", esc_sql( $trn_types ) ) . "'";

$this->add_sql_clause( 'select', 'SUM(debit) - SUM(credit) AS earnings' );
$this->add_sql_clause( 'from', $this->get_table_name_with_prefix() );
$this->add_sql_clause( 'where', $wpdb->prepare( ' AND vendor_id = %d', $vendor_id ) );
$this->add_sql_clause( 'where', $wpdb->prepare( ' AND DATE(balance_date) <= %s ', $on_date ) );
$this->add_sql_clause( 'where', " AND trn_type IN ($trn_types_str)" );
$this->add_sql_clause( 'where', " AND status IN ($statuses_str)" );

$query_statement = $this->get_query_statement();

// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$earnings = $wpdb->get_var(
$query_statement
);

return floatval( $earnings );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor complex method for better maintainability.

The get_total_earning_by_vendor method handles multiple responsibilities. Consider breaking it down into smaller, focused methods:

  1. Status handling
  2. Transaction type handling
  3. Query building

Here's a suggested refactor:

private function get_earning_status(int $vendor_id, DateTimeImmutable $balance_date): array {
    $earning_status = dokan_withdraw_get_active_order_status();
    $earning_status[] = 'approved';
    return apply_filters('dokan_vendor_total_balance_order_status', $earning_status, $vendor_id, $balance_date);
}

private function get_transaction_types(int $vendor_id, DateTimeImmutable $balance_date): array {
    $trn_types = [
        VendorBalance::TRN_TYPE_DOKAN_ORDERS,
        VendorBalance::TRN_TYPE_DOKAN_REFUND,
    ];
    return apply_filters('dokan_vendor_total_balance_trn_types', $trn_types, $vendor_id, $balance_date);
}

private function build_query(int $vendor_id, string $on_date, array $earning_status, array $trn_types): string {
    global $wpdb;
    
    $status_placeholders = array_fill(0, count($earning_status), '%s');
    $statuses_str = $wpdb->prepare(implode(',', $status_placeholders), $earning_status);
    
    $type_placeholders = array_fill(0, count($trn_types), '%s');
    $trn_types_str = $wpdb->prepare(implode(',', $type_placeholders), $trn_types);
    
    $this->add_sql_clause('select', 'SUM(debit) - SUM(credit) AS earnings');
    $this->add_sql_clause('from', $this->get_table_name_with_prefix());
    $this->add_sql_clause('where', $wpdb->prepare(' AND vendor_id = %d', $vendor_id));
    $this->add_sql_clause('where', $wpdb->prepare(' AND DATE(balance_date) <= %s ', $on_date));
    $this->add_sql_clause('where', " AND trn_type IN ($trn_types_str)");
    $this->add_sql_clause('where', " AND status IN ($statuses_str)");
    
    return $this->get_query_statement();
}

public function get_total_earning_by_vendor($vendor_id, DateTimeImmutable $balance_date): float {
    global $wpdb;
    
    $on_date = $balance_date->format('Y-m-d');
    $earning_status = $this->get_earning_status($vendor_id, $balance_date);
    $trn_types = $this->get_transaction_types($vendor_id, $balance_date);
    
    $query = $this->build_query($vendor_id, $on_date, $earning_status, $trn_types);
    return floatval($wpdb->get_var($query));
}
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[warning] 89-89:
Use placeholders and $wpdb->prepare(); found $query_statement

Comment on lines +201 to +203
public function get_credit( string $context = 'view' ) {
return floatval( $this->get_prop( 'credit', $context ) );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add missing return type declarations.

Several getter methods are missing return type declarations. Add them for consistency:

-public function get_credit( string $context = 'view' ) {
+public function get_credit( string $context = 'view' ): float {

-public function get_trn_date( string $context = 'view' ) {
+public function get_trn_date( string $context = 'view' ): ?\WC_DateTime {

-public function get_balance_date( string $context = 'view' ) {
+public function get_balance_date( string $context = 'view' ): ?\WC_DateTime {

Also applies to: 241-243, 261-263

Comment on lines +904 to +906
$long_text = _n( '%1$s rating from %2$d review', '%1$s rating from %2$d reviews', $rating['count'], 'dokan-lite' );
$text = sprintf( __( 'Rated %1$s out of %2$d', 'dokan-lite' ), $rating['rating'], number_format( 5 ) );
$width = ( $rating['rating'] / 5 ) * 100;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add translator comments for string placeholders.

Add translator comments to clarify the meaning of placeholders in the translation strings:

+/* translators: 1: rating value, 2: number of reviews */
 $long_text   = _n( '%1$s rating from %2$d review', '%1$s rating from %2$d reviews', $rating['count'], 'dokan-lite' );
+/* translators: 1: rating value, 2: maximum rating */
 $text        = sprintf( __( 'Rated %1$s out of %2$d', 'dokan-lite' ), $rating['rating'], number_format( 5 ) );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$long_text = _n( '%1$s rating from %2$d review', '%1$s rating from %2$d reviews', $rating['count'], 'dokan-lite' );
$text = sprintf( __( 'Rated %1$s out of %2$d', 'dokan-lite' ), $rating['rating'], number_format( 5 ) );
$width = ( $rating['rating'] / 5 ) * 100;
/* translators: 1: rating value, 2: number of reviews */
$long_text = _n( '%1$s rating from %2$d review', '%1$s rating from %2$d reviews', $rating['count'], 'dokan-lite' );
/* translators: 1: rating value, 2: maximum rating */
$text = sprintf( __( 'Rated %1$s out of %2$d', 'dokan-lite' ), $rating['rating'], number_format( 5 ) );
$width = ( $rating['rating'] / 5 ) * 100;
🧰 Tools
🪛 GitHub Check: Run PHPCS inspection

[failure] 904-904:
A function call to _n() with texts containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.


[failure] 905-905:
A function call to __() with texts containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (5)
docs/data-store.md (5)

9-14: Enhance documentation with data validation and error handling sections

While the overview effectively explains the data abstraction, consider adding sections about:

  • Data validation strategies
  • Error handling patterns
  • Transaction management

Would you like me to provide a template for these additional sections?


50-56: Add validation and return type declarations to name getter/setter

The name property methods should include validation and proper type declarations.

-    public function get_name( $context = 'view' ) {
+    /**
+     * Get department name
+     *
+     * @param string $context What the value is for. Valid values are 'view' and 'edit'.
+     * @return string
+     */
+    public function get_name( string $context = 'view' ): string {
         return $this->get_prop('name', $context);
     }

-    public function set_name( $name ) {
+    /**
+     * Set department name
+     *
+     * @param string $name Department name
+     * @return void
+     */
+    public function set_name( string $name ): void {
+        if ( empty( $name ) ) {
+            throw new \InvalidArgumentException( 'Department name cannot be empty' );
+        }
         return $this->set_prop( 'name', $name );
     }

116-122: Consider adding created_by and updated_by fields

For better audit trails, consider tracking who creates and updates department records.

     public function get_fields_with_format(): array {
         return [
             'name' => '%s',
             'date_created' => '%s',
             'updated_at' => '%s',
+            'created_by' => '%d',
+            'updated_by' => '%d',
         ];
     }

130-154: Add examples for common date format scenarios

The date format override section would benefit from examples of common use cases:

  • Working with different timezones
  • Handling UTC conversions
  • MySQL DATETIME vs TIMESTAMP considerations

Would you like me to provide example code for these scenarios?

🧰 Tools
🪛 LanguageTool

[uncategorized] ~153-~153: You might be missing the article “the” here.
Context: ...efault, the ID field is set to id and format is %d. ## Uses of Models ### Create...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


157-175: Expand usage examples with error handling and bulk operations

Consider adding examples for:

  • Error handling and exception catching
  • Bulk create/update operations
  • Working with relationships between models

Would you like me to provide example code for these scenarios?

🧰 Tools
🪛 LanguageTool

[style] ~157-~157: ‘New Record’ might be wordy. Consider a shorter alternative.
Context: ... %d. ## Uses of Models ### Create a New Record ```php $department = new Department(); ...

(EN_WORDINESS_PREMIUM_NEW_RECORD)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 774843d and 8417e49.

📒 Files selected for processing (1)
  • docs/data-store.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/data-store.md

[uncategorized] ~20-~20: Loose punctuation mark.
Context: ..., such as product. - protected $data: Holds the default data for the object. ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~105-~105: Loose punctuation mark.
Context: ...e following methods: - get_table_name: Defines the table name in the database....

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~106-~106: Loose punctuation mark.
Context: ...the database. - get_fields_with_format: Returns the fields with format as an ar...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~106-~106: Possible missing comma found.
Context: ...`: Returns the fields with format as an array where key is the db field name and valu...

(AI_HYDRA_LEO_MISSING_COMMA)


[typographical] ~106-~106: Two consecutive dots
Context: ...he db field name and value is the format.. Sample implementation: ```php class D...

(DOUBLE_PUNCTUATION)


[uncategorized] ~153-~153: You might be missing the article “the” here.
Context: ...efault, the ID field is set to id and format is %d. ## Uses of Models ### Create...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[style] ~157-~157: ‘New Record’ might be wordy. Consider a shorter alternative.
Context: ... %d. ## Uses of Models ### Create a New Record ```php $department = new Department(); ...

(EN_WORDINESS_PREMIUM_NEW_RECORD)


[uncategorized] ~182-~182: You might be missing the article “the” here.
Context: ... by overriding the following methods in Data Store. - `protected function map_model_...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~183-~183: Loose punctuation mark.
Context: ..._to_db_data( BaseModel &$model ): array`: Prepare data for saving a BaseModel to ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~184-~184: Loose punctuation mark.
Context: ...e database. - map_db_raw_to_model_data: Maps database raw data to model data.

(UNLIKELY_OPENING_PUNCTUATION)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (4)
tests/php/src/Models/VendorBalanceModelTest.php (4)

62-87: Add PHPDoc to the test method

The test method is well structured but missing documentation.

Add this PHPDoc:

+    /**
+     * Test reading a vendor balance record
+     *
+     * @since 3.x.x
+     * @return void
+     */
     public function test_read_method() {

89-109: Add PHPDoc to the test method

The test method is well structured but missing documentation.

Add this PHPDoc:

+    /**
+     * Test updating a vendor balance record by transaction
+     *
+     * @since 3.x.x
+     * @return void
+     */
     public function test_update_transaction_method() {

111-136: Enhance test documentation and assertions

  1. Missing PHPDoc
  2. Could verify more fields in the created balance record

Apply these improvements:

+    /**
+     * Test order synchronization creates vendor balance record
+     *
+     * @since 3.x.x
+     * @return void
+     */
     public function test_dokan_sync_insert_order_function() {
         // ... existing code ...
         $this->assertDatabaseCount(
             'dokan_vendor_balance', 1, [
                 'trn_id' => $order_id,
                 'trn_type' => VendorBalance::TRN_TYPE_DOKAN_ORDERS,
                 'vendor_id' => $this->seller_id1,
+                'status' => 'wc-pending',  // verify initial status
+                'credit' => 0,  // verify no credit
             ]
         );
     }

144-146: Remove debug code and unused variable

  1. Remove the commented debug code
  2. Remove unused variable $trn_id

Apply this diff:

-        $trn_id = 1;
-        // var_dump( $earings_data );

         foreach ( $earings_data as $earning_data ) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8417e49 and cb6b364.

📒 Files selected for processing (1)
  • tests/php/src/Models/VendorBalanceModelTest.php (1 hunks)
🔇 Additional comments (1)
tests/php/src/Models/VendorBalanceModelTest.php (1)

14-31: ⚠️ Potential issue

Fix incomplete test setup and assertions

  1. The test is missing vendor_id which appears to be a required field
  2. The debit assertion is commented out
  3. Inconsistent indentation in assertDatabaseCount array

Apply this diff:

+    /**
+     * Test saving a vendor balance record
+     *
+     * @since 3.x.x
+     * @return void
+     */
     public function test_save_method() {
         $vendor_balance = new VendorBalance();
+        $vendor_balance->set_vendor_id( 1 );
         $vendor_balance->set_debit( 100 );
         $vendor_balance->set_trn_id( 1 );
         $vendor_balance->set_trn_type( VendorBalance::TRN_TYPE_DOKAN_ORDERS );
         $vendor_balance->set_particulars( 'test' );
         $vendor_balance->save();

         $this->assertNotEmpty( $vendor_balance->get_id() );
         $this->assertDatabaseCount(
             'dokan_vendor_balance', 1, [
-				'id' => $vendor_balance->get_id(),
-				// 'debit' => 100,
-                'trn_id' => 1,
-                'trn_type' => VendorBalance::TRN_TYPE_DOKAN_ORDERS,
+                'id' => $vendor_balance->get_id(),
+                'vendor_id' => 1,
+                'debit' => 100,
+                'trn_id' => 1,
+                'trn_type' => VendorBalance::TRN_TYPE_DOKAN_ORDERS,
             ]
         );
     }

Likely invalid or redundant comment.

Comment on lines +51 to +60
public function test_delete_method() {
$vendor_balance = new VendorBalance();
$vendor_balance->save();
$id = $vendor_balance->get_id();
$this->assertNotEmpty( $vendor_balance->get_id() );

$vendor_balance->delete();

$this->assertDatabaseCount( 'dokan_vendor_balance', 0, [ 'id' => $id ] );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add required field setup

The test is missing required field setup (vendor_id) which could affect the test's validity.

Apply this diff:

     public function test_delete_method() {
         $vendor_balance = new VendorBalance();
+        $vendor_balance->set_vendor_id( 1 );
         $vendor_balance->save();

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +33 to +49
public function test_update_method() {
$vendor_balance = new VendorBalance();
$vendor_balance->save();

$this->assertEmpty( $vendor_balance->get_particulars() );

// Change the particulars
$vendor_balance->set_particulars( 'test123' );
$vendor_balance->save();

// Assert changes are applied to the database.
$this->assertDatabaseHas(
'dokan_vendor_balance', [
'perticulars' => 'test123',
]
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance test coverage and setup

The test is missing:

  1. Required field setup (vendor_id)
  2. Initial state verification
  3. Multiple field updates verification

Apply this diff:

+    /**
+     * Test updating a vendor balance record
+     *
+     * @since 3.x.x
+     * @return void
+     */
     public function test_update_method() {
         $vendor_balance = new VendorBalance();
+        $vendor_balance->set_vendor_id( 1 );
+        $vendor_balance->set_debit( 100 );
         $vendor_balance->save();

+        // Verify initial state
+        $this->assertEquals( 1, $vendor_balance->get_vendor_id() );
+        $this->assertEquals( 100, $vendor_balance->get_debit() );
         $this->assertEmpty( $vendor_balance->get_particulars() );

-        // Change the particulars
+        // Update multiple fields
+        $vendor_balance->set_vendor_id( 2 );
+        $vendor_balance->set_debit( 200 );
         $vendor_balance->set_particulars( 'test123' );
         $vendor_balance->save();

         // Assert changes are applied to the database.
         $this->assertDatabaseHas(
             'dokan_vendor_balance', [
-                'perticulars' => 'test123',
+                'vendor_id' => 2,
+                'debit' => 200,
+                'particulars' => 'test123',
             ]
         );
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function test_update_method() {
$vendor_balance = new VendorBalance();
$vendor_balance->save();
$this->assertEmpty( $vendor_balance->get_particulars() );
// Change the particulars
$vendor_balance->set_particulars( 'test123' );
$vendor_balance->save();
// Assert changes are applied to the database.
$this->assertDatabaseHas(
'dokan_vendor_balance', [
'perticulars' => 'test123',
]
);
}
/**
* Test updating a vendor balance record
*
* @since 3.x.x
* @return void
*/
public function test_update_method() {
$vendor_balance = new VendorBalance();
$vendor_balance->set_vendor_id( 1 );
$vendor_balance->set_debit( 100 );
$vendor_balance->save();
// Verify initial state
$this->assertEquals( 1, $vendor_balance->get_vendor_id() );
$this->assertEquals( 100, $vendor_balance->get_debit() );
$this->assertEmpty( $vendor_balance->get_particulars() );
// Update multiple fields
$vendor_balance->set_vendor_id( 2 );
$vendor_balance->set_debit( 200 );
$vendor_balance->set_particulars( 'test123' );
$vendor_balance->save();
// Assert changes are applied to the database.
$this->assertDatabaseHas(
'dokan_vendor_balance', [
'vendor_id' => 2,
'debit' => 200,
'particulars' => 'test123',
]
);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Progress The issues is being worked on
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants