Skip to content

Conversation

neznaika0
Copy link
Contributor

@neznaika0 neznaika0 commented Sep 6, 2025

Description
Updated PHPDoc, clarified arrays, duplicate comments have been removed from the Model and did some work with dots.
I generated the API using phpDocumentor. I didn't notice any serious problems.

Some questions:

  1. I did not find a test for sampling from the array $id for BaseModel::doFind()
  2. Can someone explain the phrase? Can it be improved?

    This method works only with dbCalls

  3. phpDocumentor does not understand conditional values for @return, it is necessary to duplicate complex conditions in @phpstan-return
  4. Look at doFindColumn() findColumn() - not sure about the type accuracy
  5. ⚠️ BaseModel::getIdValue() does not return an array. Right? I have removed PHPDoc and array checking. Is it possible to use 0, '0' for ID? Otherwise, I would have added the checks to BaseModel::shouldUpdate().
  6. Tell me if you need to update the guide or changelog.

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

@neznaika0 neznaika0 force-pushed the refactor/model-types branch from 42136ab to 11a3252 Compare September 6, 2025 16:29
Copy link
Member

@michalsn michalsn left a comment

Choose a reason for hiding this comment

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

  1. Some methods have a $testing parameter, which set to true, will not execute the query and just return the SQL.

  2. No array - as we do not support composite keys in the model right now. Support for 0 and '0' is problematic, and I would rather not allow it, as it will cause problems when combined with $db->insertID(), which uses 0 to indicate a failure.

* Used by asArray() and asObject() to provide
* The temporary format of the result.
*
* Used by `$this->asArray()` and `$this->asObject()` to provide
Copy link
Member

Choose a reason for hiding this comment

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

Why are you adding $this everywhere? It makes the comments longer and doesn't add value (at least from my perspective). Do backticks actually change the syntax in phpDocumentor? If yes, please use them consistently everywhere - if not, please remove them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, phpDocumentor supports Markdown. Therefore, we can mark some code as bold... I added a clarification to explicitly indicate a method rather than a function. This may seem redundant, but it's not a problem to reduce it. We're considering a PR together to address this.

@neznaika0
Copy link
Contributor Author

  1. Some methods have a $testing parameter, which set to true, will not execute the query and just return the SQL.
  2. No array - as we do not support composite keys in the model right now. Support for 0 and '0' is problematic, and I would rather not allow it, as it will cause problems when combined with $db->insertID(), which uses 0 to indicate a failure.
  1. I don't need to change the message?
  2. not 3? I'll add a check 0? You have an issue Possible bug: Model functions (doDelete, doFirst, etc..) ignores zero as id #9577 where it's found: that in general, the $id check is different in methods. Is it acceptable to move it to a separate method if possible?

Copy link
Member

@michalsn michalsn left a comment

Choose a reason for hiding this comment

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

  1. The message can stay, for reference.

*
* @throws InvalidArgumentException
*/
protected function hasRationalPrimaryKey($id): bool
Copy link
Member

Choose a reason for hiding this comment

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

The primary key in our model refers more to the column name, so this may be misleading.

I have no idea how we can name it... isUsableValueId()? 🤷‍♂️

The goal for these checks is purely to satisfy PHPStan or validate the allowed data?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Initially, I remove phpstan errors. The next step was formed by itself - we can validate the ID value.

I would set the relationship to PK. For now, all checks are used for it. But it can be renamed to hasRationalId() if it is used for table joins, for example.

Copy link
Member

Choose a reason for hiding this comment

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

The "rational" part of the method doesn't really speak to me, to be honest.

Well... let's forget about validating the primary key for now - I'm not convinced we need to do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Remove the check or what, back to if ($id)?

rational - based on logic. If there is a problem with the translation.

Copy link
Member

Choose a reason for hiding this comment

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

Using if ($id) would likely bring us back to square one, with PHPStan raising an error.

Yes, I know what “rational” means, but I don't think it's a good fit here.

I'm leaning towards leaving the code as it is (without introducing an additional method), but unifying the values in in_array. Happy to hear others' opinions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@samsonasik @paulbalandan i need help.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, what was the issue? Is it the underlying code or the method naming? Or both?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@paulbalandan The question is about the usefulness of the method. I would check the ID for a correct value. Since earlier the check was if ($id) {}

If the ID may not always be PK, let's rename it to hasAllowedValueId() and otherwise. I cannot say for all cases where the ID is used (Postgres, OCI8..), at the moment this check is acceptable.

@github-actions github-actions bot added the stale Pull requests with conflicts label Sep 10, 2025
Copy link

👋 Hi, @neznaika0!

We detected conflicts in your PR against the base branch 🙊
You may want to sync 🔄 your branch with upstream!

Ref: Syncing Your Branch

* @param non-empty-string|null $key
* @param array|BaseBuilder|(Closure(BaseBuilder): BaseBuilder)|null $values The values searched on, or anonymous function with subquery
* @param non-empty-string|null $key
* @param array<int|string, >|BaseBuilder|(Closure(BaseBuilder): BaseBuilder)|null $values The values searched on, or anonymous function with subquery
Copy link
Member

Choose a reason for hiding this comment

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

Looks like the array is missing the value type?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a list of IDs for whereIn(). Do you think it's acceptable to have string keys or just a list? As $qb->whereIn('user.id', [0 => 123, 'user_id2' => 134])

* used during data validation.
*
* @var array<string, array<string, string>>
* @var array<string, array<string, string>> The column is used as the keys.
Copy link
Member

Choose a reason for hiding this comment

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

For this @var and the previous ones: does @var accept a description after the type?

Copy link
Contributor Author

@neznaika0 neznaika0 Oct 4, 2025

Choose a reason for hiding this comment

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

image Yes. But it looks like a typo. Although the `DataCaster` has the same principle. I can leave it.

Comment on lines +422 to +423
* @return list<object|row_array>|object|row_array|null The resulting row of data or null.
* @phpstan-return ($singleton is true ? object|row_array|null : list<object|row_array>)
Copy link
Member

Choose a reason for hiding this comment

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

this was done because phpdocumentor doesn't understand conditional return types, right? then, does it also understand custom phpstan types like this row_array? If not, then @return should be all native PHPDoc and @phpstan-return should be the conditional return + phpstan-type

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See https://codeigniter4.github.io/api/classes/CodeIgniter-Model.html#method_doInsert
Yes, I misunderstood and changed @phpstan-param. You need to return individual comments. Thanks.

Copy link
Contributor Author

@neznaika0 neznaika0 Oct 4, 2025

Choose a reason for hiding this comment

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

@paulbalandan, Now it's unclear how to replace row_array? phpDocumetor doesn't understand it, but if I replace it with list<object|array<int|string, float|int|null|object|string|bool>>|object| array<int|string, float|int|null|object|string|bool>|null It's no better. Any suggestions? The type is used in many places

* @param string $columnName Column Name
*
* @return array|null The resulting row of data, or null if no data found.
* @return list<bool|float|int|list<mixed>|object|string|null>|null The resulting row of data, or null if no data found
Copy link
Member

Choose a reason for hiding this comment

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

Can this be just list<mixed>? I really see no point in expanding the mixed type to its long form if we cannot further refine the types by reducing the types in union. This is just verbose.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In some PR kenjis asked to replace mixed with a similar list. If possible, I'll shorten it.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, with the intention of shortening the union to the actual types. Historically, IDEs would add the mixed phpdoc if the structural element has no type.

If the change was mixed -> string|int|null (for example) because those were the actual types and not the all-encompassing mixed then the change is useful. If the change was mixed -> array|bool|int|float|object|string|null with no type refinement because the element actually can take anything or unknown, then I would be fine with sticking to mixed as the short form.

*
* @throws InvalidArgumentException
*/
protected function hasAllowedValueId($id): bool
Copy link
Member

Choose a reason for hiding this comment

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

We're in PHP 8.1 and this is a new method so we can safely add the parameter types so that the is_bool check below is no longer needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The type is too extensive. list|int|string|null You're right, i can delete it, because checking against the list of acceptable values.

* Especially the values 0, '0', '' are rarely used or are invalid.
* Example, if you add an entry with ID=0, it will be an error for `$this->insertID`.
*
* @param array<int|string, int|string>|float|int|string|null $id
Copy link
Member

Choose a reason for hiding this comment

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

is float allowed as PK? would this be just int|string or a list of them? also i don't see a case where you pass a string-keyed array in here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok. Changed

}

/**
* Checking that the ID has a rational value.
Copy link
Member

Choose a reason for hiding this comment

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

I would propose naming this method as isIdAllowedAsPrimaryKey() as that is really the purpose of this method. Or maybe isIdValidAsPrimaryKey() something.

throw new InvalidArgumentException('The ID value should not be boolean.');
}

return ! in_array($id, [0, '0', '', 0.0], true) && (is_numeric($id) || is_string($id));
Copy link
Member

Choose a reason for hiding this comment

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

Depending on what the @param should actually be in above, I would propose to do the check using if guard style. More verbose but clearer I believe.

$pkCandidates = is_array($id) ? $id : [$id];

foreach ($pkCandidates as $candidate) {
    if (is_int($candidate) && $candidate === 0) {
        return false;
    }

    if (is_string($candidate) && in_array($candidate, ['0', ''], true)) {
        return false;
    }
}

return true;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stale Pull requests with conflicts
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants