How to build self referencing tables in Symfony using Propel ORM - mysql

I have an error trying to build a model from an existing database in a symfony project using the Propel ORM.
The error is this:
build-propel.xml:474:20: The 1:1 relationship expressed by foreign key a_table_on_my_schema_FK_1 is defined in both directions; Propel does not currently support this (if you must have both foreign key constraints, consider adding this constraint with a custom SQL file.)
the schema.yml file is really extensive but the description of the table that causes the error (the first not correctly created) is like this:
self_referenced_table:
_attributes: { phpName: SelfReferencedTable }
[...]
JERARQUIC_CODE: { phpName: JerarquicCode, type: INTEGER, size: '8', required: false, foreignTable: self_referenced_table, foreignReference: JERARQUIC_CODE, onDelete: RESTRICT, onUpdate: RESTRICT }
[...]
I think this error is because of the self referenced table.
I need to implement a jerarquic relation between many elements so this implementation is a good way to do it. But causes me this problem on construction.
Can you give me some clues? have someone had this error? what would you do?
thank you!! :D

Solved: It was not a self referencing table error, as said by #Colin Fine. The error was on the source database. I generated the schema.yml from an existing database on mysql. The error was there: the target attribute of the reference was not the identifier of the table, was the reference attribute itself. So, the generated schema.yml contained wrong definitions. I think i havn't explained well enough:
self_referenced_table was that:
_attributes: { phpName: SelfReferencedTable }
[...]
JERARQUIC_CODE: { phpName: JerarquicCode, type: INTEGER, size: '8', required: false, foreignTable: self_referenced_table, foreignReference: JERARQUIC_CODE, onDelete: RESTRICT, onUpdate: RESTRICT }
[...]
self_referenced_table should be:
_attributes: { phpName: SelfReferencedTable }
[...]
JERARQUIC_CODE: { phpName: JerarquicCode, type: INTEGER, size: '8', required: false, foreignTable: self_referenced_table, foreignReference: TABLE_CODE, onDelete: RESTRICT, onUpdate: RESTRICT }
[...]

Related

Add foreign key index Loopback 4 MySQL

i am new in Loopback 4
I followed this tutorial to get started and everything worked fine.
I tried to create my own models Category and SubCategory using a MySQL database, with one to many relation (one category has many sub categories), i have noticed that it did create a field in subcategory table (categoryId) but the foreign key index is missing.
can someone help?
LoopBack 4 does not implicitly add foreign key constraints. This is to allow weak cross-datasource relations (e.g. a relation between PostgreSQL & Oracle).
Hence, the responsibility falls on the connectors to provide an interface to define these constraints. This however, means that there isn't a consistent interface across different connectors. There is an open issue to track this.
In the case of MySQL:
#model({
settings: {
foreignKeys: {
categorySubCategoryFK: {
name: 'categorySubCategoryFK',
entity: 'Category',
entityKey: 'id',
foreignKey: 'categoryId',
},
},
},
})
In case auto-migration is used (which it should not be used in production!), migrate.ts would need to be updated to define explicit ordering of the schemas:
await app.migrateSchema({
existingSchema,
models: ['Category', 'SubCategory'],
});
Further reading
https://loopback.io/doc/en/lb4/todo-list-tutorial-sqldb.html#specify-the-foreign-key-constraints-in-todo-model
https://loopback.io/doc/en/lb4/MySQL-connector.html
found the answer here, besides adding the settings to #model annotation, like so
#model({
settings: {
foreignKeys: {
categorySubCategoryFK: {
name: 'categorySubCategoryFK',
entity: 'Category',
entityKey: 'id',
foreignKey: 'categoryId',
},
},
},
})
you have to change to specify in which order tables should be created in migrate.ts and change this
await app.migrateSchema({existingSchema});
to this
await app.migrateSchema({
existingSchema,
models: ['Category', 'SubCategory'],
});
more details here
Loopback 4 creates relation on models and Api's not on database. Thus it won't get reflected on your mysql database

ORM: Sequelize: Add Not Equal Constraint for Two Columns

I have a following table. I don't want user to follow himself so I want to add a CHECK constraint.
For example, if this is inserted, I want database to throw an error.
{
userID: 1,
followingID: 1,
}
I can check with Javascript if userID and followingID are equal but I want database to check it.
My MYSQL version is 8.0.17 so I think it is possible to create this constraint with SQL. How can I add this constraint with Sequelize?
There is two solution
1. Using Model wise validation and adding check constraint in database manually:
Model wise validation:
const FollowingModel = sequelize.define("following", {
userId: {
type: Sequelize.INTEGER,
// .. other configuration like `allowNull`
},
followingId: {
type: Sequelize.INTEGER,
// .. other configuration like `allowNull`
}
}, {
validate: {
userShouldNotFollowSelf : function() {
if(this.userId === this.followingId) {
throw Error("User should not follow self") // Use any custom error class if your application has such class.
}
}
}
}
Beware this will allow you create entry in database which does not maintain this constraint.
It is just ORM's application layer check that, this application won't allow any entry where userId and followingId is not same.
Mysql database layer check constraint.
CREATE TABLE `following`
(
`userId` INT NOT NULL,
`followingId` INT NOT NULL,
CONSTRAINT `no_self_following` CHECK (`userId` <> `followingId`)
-- other properties and foreign key constraints.
);
It will ensure that, no such entry inserted where userId and followingId is same.
2. Declaring constraint in sequelize query interface.
This require to declare your model using query interfaces addConstraint as follows
sequelize.getQueryInterface().addConstraint("following", ['userId'], {
type: 'check',
name: "no_self_following"
where: {
userId: {
[Sequelize.Op.ne]: Sequelize.col("followingId")
}
}
});
Run this while all database model is been synced correctly. It will add database level constraint.
Which one to use ?
Approach #1 is more efficient. It is checking within the application without going into the database call, Makes your database less busy.

How to do joins in SequelizeJS?

This is my model definition of Room table
sequelize.define("room", {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
name: DataTypes.STRING,
business_id: {
type: DataTypes.INTEGER,
references: {
model:"business,
key: "id"
}
}
});
And this is of business
sequelize.define("business", {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
name: DataTypes.STRING,
});
Now consider I have id of the room with me and I want to get business related to that using the foreign key. In normal SQL I would easily do this using inner join
I know that there is something like Association used in Sequelize but then what is the use of defining a reference in the model definition?
As per the DOC :
Sometimes you may want to reference another table, without adding any
constraints, or associations. In that case you can manually add the
reference attributes to your schema definition, and mark the relations
between them.
Association :
Creating associations in sequelize is done by calling one of the
belongsTo / hasOne / hasMany / belongsToMany functions on a model (the
source), and providing another model as the first argument to the
function (the target).
hasOne - adds a foreign key to the target and singular association
mixins to the source.
belongsTo - add a foreign key and singular association mixins to the source.
hasMany - adds a foreign key to target and plural association mixins to the source.
belongsToMany - creates an N:M association with a join table and adds plural association mixins to the source. The junction table is
created with sourceId and targetId.
Creating an association will add a foreign key constraint to the
attributes. All associations use CASCADE on update and SET NULL on
delete, except for n:m, which also uses CASCADE on delete.
I think this will clear your doubts.

Cannot add or update a child row: a foreign key constraint fails CONSTRAINT `PageChild_Page_Id_fk` FOREIGN KEY (`PageId`) REFERENCES `Page` (`Id`)

I'm using Sails and Waterline for my model associations and I'm not sure what to do in order to fix this error I'm receiving when trying to update a PageChild object.
module.exports = {
tableName: 'Page',
adapter: 'mysql',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
Id: {type: 'integer', autoIncrement: true, primaryKey: true},
pageChildren: {
collection: 'PageChild',
via: 'Page'
}
},
};
module.exports = {
tableName: 'PageChild',
adapter: 'mysql',
attributes: {
Id: {type: 'integer', autoIncrement: true, primaryKey: true},
Page: {
model: 'Page',
columnName: 'PageId'
}
}
};
The model associations work perfectly fine for populating pageChildren from the Page object or for returning the Page object from any of the pageChildren. However, I encounter this foreign key issue when attempting to create or update a PageChild object.
In the mysql db, the Page table has an "Id" attribute while the PageChild table has "Id" and "PageId" attributes.
The error is self explanatory:
foreign key constraint fails CONSTRAINT `PageChild_Page_Id_fk` FOREIGN KEY (`PageId`) REFERENCES `Page` (`Id`)
The rule is, you can only add or update a value in child table which are already present in parent table. So at the time of insertion make sure the value you are trying to insert in child table, already exist in parent table.
What this means is that the ParentId that you add or update on the child row needs to exist on the Parent table.
So, this constraint means that you can't insert a row into PageChild with PageId = 50 if you don't have a row in Page that has an id with a value of 50.
For example, if you want to create a new page, you have to first create an entry in the Page table, then retrieve it's id value and only then can you start doing your inserts into the PageChild table using the id of the Page you made earlier.

One to two relationship in Doctrine with YAML

I'm working on my first Symfony project with Doctrine, and I've run into a hitch. I'm trying to express a game with two players. The relationship I want to have is PlayerOne and PlayerTwo each being keyed to an ID in the Users table. This is part of what I've got so far:
Game:
actAs: { Timestampable:- }
columns:
id: { type: integer, notnull: true, unique: true }
startDate: { type: timestamp, notnull: true }
playerOne: { type: integer, notnull: true }
playerTwo: { type: integer, notnull: true }
winner: { type: integer, notnull:true, default:0 }
relations:
User: { onUpdate: cascade, local: playerOne, foreign: id}
User: { onUpdate: cascade, local: playerTwo, foreign: id}
That doesn't work. It builds fine, but the SQL it generates only includes a constraint for playerTwo. I've tried a few other things:
User: { onUpdate: cascade, local: [playerOne, playerTwo], foreign: id}
Also:
User: [{ onUpdate: cascade, local: playerOne, foreign: id}, { onUpdate: cascade, local: playerTwo, foreign: id}]
Those last two throw errors when I try to build. Is there anyone out there who understands what I'm trying to do and can help me achieve it?
Try different names for relations:
relations:
UserOne: { onUpdate: cascade, local: playerOne, foreign: id, class: User }
UserTwo: { onUpdate: cascade, local: playerTwo, foreign: id, class: User }
Edited: also note added "class: User" parameter, which explicitly tells about type of the relation.
If you say $game->getUser(), it's not clear which user you're referring to. Because they reference the same table, you need to give the two relations different names and declare two different foreignAliases for them. Basically, your game table needs to be able to refer to each relation with a different name (e.g. UserOne, UserTwo) and vice versa. It'll confuse Doctrine otherwise and relations won't be set up correctly.
I had a similar problem earlier... using MySQL Workbench to generate a visual representation of the schema after building the models helped make sure the relations were all exactly as needed.
Hope that helps.