Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions examples/orders/models/Order.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ class Order extends ActiveRecord\Model
{
// order belongs to a person
public static array $belongs_to = [
['person']];
'person' => true
];

// order can have many payments by many people
// the conditions is just there as an example as it makes no logical sense
public static $has_many = [
['payments'],
['people',
public static array $has_many = [
'payments' => true,
'people' => [
'through' => 'payments',
'select' => 'people.*, payments.amount',
'conditions' => 'payments.amount < 200']];
'conditions' => 'payments.amount < 200'
]
];

// order must have a price and tax > 0
public static array $validates_numericality_of = [
Expand Down
5 changes: 3 additions & 2 deletions examples/orders/models/Payment.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class Payment extends ActiveRecord\Model
{
// payment belongs to a person
public static array $belongs_to = [
['person'],
['order']];
'person' => true,
'order' => true
];
}
7 changes: 4 additions & 3 deletions examples/orders/models/Person.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
class Person extends ActiveRecord\Model
{
// a person can have many orders and payments
public static $has_many = [
['orders'],
['payments']];
public static array $has_many = [
'orders' => true,
'payments'=> true
];

// must have a name and a state
public static array $validates_presence_of = [
Expand Down
47 changes: 34 additions & 13 deletions lib/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use ActiveRecord\Exception\UndefinedPropertyException;
use ActiveRecord\Relationship\AbstractRelationship;
use ActiveRecord\Relationship\HasAndBelongsToMany;
use ActiveRecord\Relationship\HasMany;
use ActiveRecord\Serialize\JsonSerializer;
use ActiveRecord\Serialize\Serialization;

Expand All @@ -39,14 +40,20 @@
*
* ```php
* class Person extends ActiveRecord\Model {
* static array $belongs_to = array(
* array('parent', 'foreign_key' => 'parent_id', 'class_name' => 'Person')
* );
* static array $belongs_to = [
* 'parent' => [
* 'foreign_key' => 'parent_id',
* 'class_name' => 'Person'
* ]
* ];
*
* static $has_many = array(
* array('children', 'foreign_key' => 'parent_id', 'class_name' => 'Person'),
* array('orders')
* );
* static array $has_many = [
* 'children' => [
* 'foreign_key' => 'parent_id',
* 'class_name' => 'Person'
* ],
* 'orders' => true
* ];
*
* static $validates_length_of = [
* 'first_name' => ['within' => [1,50]],
Expand All @@ -56,7 +63,7 @@
*
* class Order extends ActiveRecord\Model {
* static array $belongs_to = [
* 'person'
* 'person' => true
* ];
*
* static $validates_numericality_of = [
Expand All @@ -75,6 +82,8 @@
* For a more in-depth look at defining models, relationships, callbacks and many other things
* please consult our {@link http://www.phpactiverecord.org/guides Guides}.
*
* @phpstan-import-type HasManyOptions from Types
* @phpstan-import-type BelongsToOptions from Types
* @phpstan-import-type SerializeOptions from Serialize\Serialization
* @phpstan-import-type ValidationOptions from Validations
* @phpstan-import-type ValidateInclusionOptions from Validations
Expand Down Expand Up @@ -190,12 +199,12 @@ class Model
public static array $validates_format_of;

/**
* @var ValidateInclusionOptions
* @var array<string,ValidateInclusionOptions>
*/
public static array $validates_inclusion_of;

/**
* @var ValidateInclusionOptions
* @var array<string,ValidateInclusionOptions>
*/
public static array $validates_exclusion_of;

Expand All @@ -214,6 +223,16 @@ class Model
*/
public static array $validates_length_of;

/**
* @var array<string,HasManyOptions>
*/
public static array $has_many;

/**
* @var array<string,BelongsToOptions>
*/
public static array $belongs_to;

/**
* Allows you to create aliases for attributes.
*
Expand Down Expand Up @@ -272,8 +291,11 @@ class Model
*
* ```
* class Person extends ActiveRecord\Model {
* static array $belongs_to = array(array('venue'),array('host'));
* static $delegate = array(
* static array $belongs_to = [
* 'venue' => true,
* 'host' => true
* ];
* static $delegate = array(
* array('name', 'state', 'to' => 'venue'),
* array('name', 'to' => 'host', 'prefix' => 'woot'));
* }
Expand Down Expand Up @@ -1722,7 +1744,6 @@ public static function last(/* ... */)
* "all" static[] User::find("all", ["name"=>"Stephen"]
* ...int|string static[] User::find(1, 3, 5, 8);
* array<int,int|string> static[] User::find([1,3,5,8]);
* array<"conditions", array<string, string>> static[] User::find(["conditions"=>["name"=>"Kurt"]]);
*/
public static function find(/* $type, $options */): static|array|null
{
Expand Down
3 changes: 1 addition & 2 deletions lib/PhpStan/FindDynamicMethodReturnTypeReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection,

if (1 == $numArgs) {
if (!($args[0] instanceof ConstantArrayType)
|| ('conditions' != $args[0]->getKeyTypes()[0]->getValue()
&& !$this->isNumericArray($args[0]))) {
|| (!$this->isNumericArray($args[0]))) {
$single = true;
}
} elseif ($numArgs > 1) {
Expand Down
4 changes: 2 additions & 2 deletions lib/Relationship/AbstractRelationship.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ abstract class AbstractRelationship
*
* @param array<mixed> $options Options for the relationship (see {@link valid_association_options})
*/
public function __construct($options = [])
public function __construct(string $attribute_name, $options = [])
{
$this->attribute_name = $options[0];
$this->attribute_name = $attribute_name;
$this->options = $this->merge_association_options($options);

$relationship = strtolower(denamespace(get_called_class()));
Expand Down
21 changes: 12 additions & 9 deletions lib/Relationship/BelongsTo.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use ActiveRecord\Inflector;
use ActiveRecord\Model;
use ActiveRecord\Table;
use ActiveRecord\Types;

/**
* Belongs to relationship.
Expand All @@ -13,9 +14,9 @@
* class School extends ActiveRecord\Model {}
*
* class Person extends ActiveRecord\Model {
* static array $belongs_to = array(
* array('school')
* );
* static array $belongs_to = [
* 'school' => true
* ];
* }
* ```
*
Expand All @@ -25,16 +26,18 @@
* class School extends ActiveRecord\Model {}
*
* class Person extends ActiveRecord\Model {
* static array $belongs_to = array(
* array('school', 'primary_key' => 'school_id')
* );
* static array $belongs_to = [
* 'school' => [
* 'primary_key' => 'school_id'
* ]
* ]
* }
* ```
*
* @phpstan-import-type BelongsToOptions from Types
* @phpstan-import-type Attributes from Model
*
* @see valid_association_options
* @see http://www.phpactiverecord.org/guides/associations
*/
class BelongsTo extends AbstractRelationship
{
Expand All @@ -55,9 +58,9 @@ public function primary_key(): array
return $this->primary_key;
}

public function __construct($options = [])
public function __construct(string $attributeName, $options = [])
{
parent::__construct($options);
parent::__construct($attributeName, $options);

if (!$this->class_name) {
$this->set_inferred_class_name();
Expand Down
2 changes: 1 addition & 1 deletion lib/Relationship/HasAndBelongsToMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function __construct($options = [])
* uniq - if true duplicate assoc objects will be ignored
* validate
*/
parent::__construct($options);
parent::__construct($options[0], $options);
}

public function load(Model $model): mixed
Expand Down
39 changes: 15 additions & 24 deletions lib/Relationship/HasMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use ActiveRecord\Inflector;
use ActiveRecord\Model;
use ActiveRecord\Table;
use ActiveRecord\Types;

/**
* One-to-many relationship.
Expand All @@ -19,43 +20,33 @@
* # Table: schools
* # Primary key: id
* class School extends ActiveRecord\Model {
* static $has_many = array(
* array('people')
* );
* static array $has_many = [
* 'people' => true
* ];
* });
* ```
*
* Example using options:
*
* ```php
* class Payment extends ActiveRecord\Model {
* static array $belongs_to = array(
* array('person'),
* array('order')
* );
* static array $belongs_to = [
* 'person'=>true,
* 'order'=>true
* ];
* }
*
* class Order extends ActiveRecord\Model {
* static $has_many = array(
* [
* 'people',
* static array $has_many = [
* 'people' => [
* 'through' => 'payments',
* 'select' => 'people.*, payments.amount',
* 'conditions' => 'payments.amount < 200')
* ];
* }
* ]
* ```
*
* @phpstan-import-type Attributes from Model
*
* @phpstan-type HasManyOptions array{
* limit?: int,
* offset?: int,
* primary_key?: string|array<string>,
* group?: string,
* order?: string,
* through?: string
* }
* @phpstan-import-type HasManyOptions from Types
*
* @see http://www.phpactiverecord.org/guides/associations
* @see valid_association_options
Expand Down Expand Up @@ -90,11 +81,11 @@ class HasMany extends AbstractRelationship
/**
* Constructs a {@link HasMany} relationship.
*
* @param HasManyOptions $options Options for the association
* @param HasManyOptions $options
*/
public function __construct(array $options = [])
public function __construct(string $attribute, array $options = [])
{
parent::__construct($options);
parent::__construct($attribute, $options);

if (isset($this->options['through'])) {
$this->through = $this->options['through'];
Expand Down
8 changes: 4 additions & 4 deletions lib/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -580,21 +580,21 @@ private function set_associations(): void
continue;
}

foreach (wrap_values_in_arrays($definitions) as $definition) {
foreach (wrap_values_in_arrays($definitions) as $attribute => $definition) {
$relationship = null;
$definition += ['namespace' => $namespace];

switch ($name) {
case 'has_many':
$relationship = new HasMany($definition);
$relationship = new HasMany($attribute, $definition);
break;

case 'has_one':
$relationship = new HasOne($definition);
$relationship = new HasOne($definition[0], $definition);
break;

case 'belongs_to':
$relationship = new BelongsTo($definition);
$relationship = new BelongsTo($attribute, $definition);
break;

case 'has_and_belongs_to_many':
Expand Down
14 changes: 14 additions & 0 deletions lib/Types.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@
* order?: string,
* set?: string|array<string, mixed>
* }
* @phpstan-type HasManyOptions array{
* limit?: int,
* offset?: int,
* primary_key?: string|array<string>,
* group?: string,
* order?: string,
* through?: string
* }
* @phpstan-type BelongsToOptions array{
* conditions?: array<mixed>,
* foreign_key?: string,
* class_name?: class-string,
* primary_key?: string
* }
* @phpstan-type DelegateOptions array{
* to: string,
* prefix?: string,
Expand Down
4 changes: 3 additions & 1 deletion test/ActiveRecordFindTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,9 @@ public function testFindWithSelectNonSelectedFieldsShouldNotHaveAttributes()

public function testJoinsOnModelWithAssociationAndExplicitJoins()
{
JoinBook::$belongs_to = [['author']];
JoinBook::$belongs_to = [
'author' => true
];
JoinBook::first(['joins' => ['author', 'LEFT JOIN authors a ON(books.secondary_author_id=a.author_id)']]);
$this->assert_sql_has('INNER JOIN authors ON(books.author_id = authors.author_id)', JoinBook::table()->last_sql);
$this->assert_sql_has('LEFT JOIN authors a ON(books.secondary_author_id=a.author_id)', JoinBook::table()->last_sql);
Expand Down
Loading