phalcon / cphalcon

High performance, full-stack PHP framework delivered as a C extension.
https://phalcon.io
BSD 3-Clause "New" or "Revised" License
10.79k stars 1.96k forks source link

hasOne foreignKey restriction not working #12443

Closed aat2703 closed 7 years ago

aat2703 commented 7 years ago

hasOne foreignKey restriction

Hi Phalconists

I'm trying to create a 1-1 foreignKey restriction between the RestaurantOrderLines and RestaurantProducts. But if we just for fun change the hasOne function call to belongsTo then it works perfectly :)

So that an RestaurantOrderLines cannot be created if the product does not exists.

Code executed:

       $OrderLine = new RestaurantOrderLines();

    $OrderLine->orderID                = 1;
    $OrderLine->productID            = 2;
    $OrderLine->quantity               = 1;
         // Returns true, and is not restricting creation even though the productID does not exist on the restaurant_products table
         $create = $OrderLine->create();

RestaurantOrderLines Model:


    public function initialize() {

       // If hasOne is changed to belongsTo then the restriction works
        $this->hasOne(
            'productID',
            'Restaurant\Model\Product\RestaurantProducts',
            'productID',
            array(
                'foreignKey' => array(
                    'message' => 'Product not found on Restaurant\Model\Product\RestaurantProducts model',
                    'action'    => Relation::ACTION_RESTRICT
                ),
                'alias' => 'Product'
            )
        );

    }

RestaurantProducts Model:


    public function initialize() {

        $this->belongsTo(
            'productID',
            'Restaurant\Model\Order\Line\RestaurantOrderLines',
            'productID',
            array(
                'alias' => 'OrderLines'
            )
        );

    }

Table schema

CREATE TABLE `restaurant_products` (
  `productID` int(45) unsigned NOT NULL AUTO_INCREMENT,
  `productCategoryID` int(11) DEFAULT NULL,
  `productName` varchar(256) CHARACTER SET latin1 DEFAULT NULL,
  `productDescription` text CHARACTER SET latin1,
  `productDescriptionHTML` text,
  `productPrice` decimal(10,2) DEFAULT '0.00',
  `sorting` int(11) DEFAULT '0',
  `state` enum('ACTIVE','PAUSED','TEMPORARY') DEFAULT 'ACTIVE',
  `createTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`productID`)
) ENGINE=InnoDB AUTO_INCREMENT=2514 DEFAULT CHARSET=utf8;
CREATE TABLE `restaurant_orderLines` (
  `orderLineID` int(11) NOT NULL AUTO_INCREMENT,
  `orderID` int(11) NOT NULL DEFAULT '0',
  `productID` int(11) unsigned DEFAULT '0',
  `orderLineName` varchar(512) DEFAULT NULL,
  `orderLineDescription` varchar(5096) DEFAULT 'Ingen tilvalg',
  `orderLinePrice` decimal(10,2) DEFAULT '0.00',
  `orderLineType` enum('PRODUCT','CUSTOM') DEFAULT 'PRODUCT',
  `quantity` int(11) DEFAULT '1',
  `plate` int(11) DEFAULT '0',
  `split` enum('PENDING','COMPLETED','FAILED') DEFAULT 'PENDING',
  `identifier` varchar(512) DEFAULT NULL,
  `discount` decimal(10,2) DEFAULT '0.00',
  `recommendedProduct` tinyint(1) DEFAULT '0',
  `kitchenApproved` tinyint(1) DEFAULT '0',
  `kitchenApprovedAt` datetime DEFAULT NULL,
  `waiterDelivered` tinyint(1) DEFAULT '0',
  `waiterDeliveredQuantity` int(11) DEFAULT '0',
  `waiterDeliveredAt` datetime DEFAULT NULL,
  `createTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`orderLineID`),
  KEY `orderID` (`orderID`),
  KEY `productID` (`productID`),
  CONSTRAINT `orderID` FOREIGN KEY (`orderID`) REFERENCES `restaurant_orders` (`orderID`)
) ENGINE=InnoDB AUTO_INCREMENT=6666 DEFAULT CHARSET=latin1;

Details

Jurigag commented 7 years ago

It's not a bug. You just don't understand for what is hasOne. There is sample:

User table

id | profile_id

On user Model:

public function initialize
{
    $this->belongsTo('profile_id', 'Profile', 'id', ['alias' => 'profile']);
}

Profile table

id | first_name | last_name

Profile model:

public function initialize()
{
    $this->hasOne('id', 'User', 'profile_id', ['alias' => 'user']);
}

So - hasOne just works same as hasMany, just returns only one model instead of resultset. It is made for unidirectional relationships. In your case since you have bidirectional relationships there should be used belongsTo in both cases.

Also it's really BAD to call productID the same as field and related field.

aat2703 commented 7 years ago

@Jurigag, i get it now!

Thank you!

Sorry for the naming conventions, i have abit of reading to do: https://launchbylunch.com/posts/2014/Feb/16/sql-naming-conventions/