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.
Related
I have been trying to define a relationship between 3 tables and then create them all in one create function. For some reason, while creating the 3 models, the linking IDs (foreign keys) are undefined and are not passing on. Here are the associations:
Person.js:
models.person.Lead = models.person.hasMany(models.lead, {
onDelete: "CASCADE",
foreignKey: "person_id"
});
Lead.js:
models.lead.Person = models.lead.belongsTo(models.person, {foreignKey: 'person_id'});
models.lead.Sealant_customer = models.lead.hasOne(models.sealant_customer, {
onDelete: "CASCADE",
foreignKey: 'lead_id'
})
sealantCustomer.js:
models.sealant_customer.Lead = models.sealant_customer.belongsTo(models.lead);
The build function:
let sealantCustomer = models.sealant_customer.build({
address: body.address,
city: body.city,
roof_size: body.roofSize,
last_sealed: body.lastSealed,
existingSealant: body.existingSealant,
leaks_freq: body.leaksFrequency,
floor: body.floor,
elevator: body.elevator,
panels: body.panels,
home_type: body.homeType,
urgency: body.urgency,
next_step: body.nextStep,
more_info: body.moreInfo,
lead: {
site,
url: body.url,
date,
ip: body.ip,
person: {
name: body.name,
email: body.email,
phone: body.phone,
date,
city: body.city ? body.city : undefined,
address: body.address ? body.address : undefined,
}
}
}, {
include: [{
model: models.lead,
association: models.sealant_customer.Lead,
include: [{
model: models.person,
association: models.lead.Person
}]
}]
})
The outputted object is good except for the fact that lead_id and person_id are nulls (Each model has its own ID, but not the associated model's id). I also should note there are no validation errors and the data is good.
The library has a bug in the build function as far as I can tell. Same syntax with create worked perfectly.
In Sequelize v6, the association identifier in the include section is not valid. Otherwise, this build function should properly work.
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.
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.
When using Sequelize.js, the following code doesn't add any foreign key on tables.
var MainDashboard = sequelize.define('main_dashboard', {
title: Sequelize.STRING
}, {
freezeTableName: true
})
MainClient.hasOne(MainDashboard, { foreignKey: 'idClient' })
MainDashboard.hasOne(MainClient, { foreignKey: 'clientId' })
sequelize.sync({ force: true })
Is there any way to force Sequelize.js to add these foreign key constraints?
Before I had the same problem, and solved when I understood the functioning of settings Sequelize.
Straight to the point!
Suppose we have two objects: Person and Father
var Person = sequelize.define('Person', {
name: Sequelize.STRING
});
var Father = sequelize.define('Father', {
age: Sequelize.STRING,
//The magic start here
personId: {
type: Sequelize.INTEGER,
references: 'persons', // <<< Note, its table's name, not object name
referencesKey: 'id' // <<< Note, its a column name
}
});
Person.hasMany(Father); // Set one to many relationship
Maybe it helps you
Edit:
You can read this to understand better:
http://docs.sequelizejs.com/manual/tutorial/associations.html#foreign-keys
For Sequelize 4 this has been updated to the following:
const Father = sequelize.define('Father', {
name: Sequelize.STRING
});
const Child = sequelize.define('Child', {
age: Sequelize.STRING,
fatherId: {
type: Sequelize.INTEGER,
references: {
model: 'fathers', // 'fathers' refers to table name
key: 'id', // 'id' refers to column name in fathers table
}
}
});
Father.hasMany(Child); // Set one to many relationship
Edit:
You can read more on associations at https://sequelize.org/master/manual/assocs.html
You need to add foreignKeyConstraint: true
Try:
MainClient.hasOne(MainDashboard, { foreignKey: 'idClient', foreignKeyConstraint: true })
I just tried to run your code, and the rows seem to be created fine:
CREATE TABLE IF NOT EXISTS `main_dashboard` (`title` VARCHAR(255), `id` INTEGER NOT NULL auto_increment , `idClient` INTEGER, PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `main_client` (`id` INTEGER NOT NULL auto_increment, `clientId` INTEGER, PRIMARY KEY (`id`)) ENGINE=InnoDB;
clientId is added to main_client, and idClient is added to main_dashboard
It seems you have slightly confused what the hasOne method does. Each time you call hasOne an association is created, so your code effectively associates the two tables twice. The method you are looking for is belongsTo
If you want each client to have one dashboard, the code would be the following:
MainClient.hasOne(MainDashboard, { foreignKey: 'clientId' })
MainDashboard.belongsTo(MainClient, { foreignKey: 'clientId' })
This creates a clientId field on the main_dashboard table, which relates to the id field of the main_client table
In short belongsTo adds the relation to the table that you are calling the method on, hasOne adds it on the table that is given as argument.
It's amazingly simple.
const MainDashboard = this.sequelize.define('main_dashboard', {/* attributes */}),
MainClient = this.sequelize.define('main_client', {/* attributes */});
MainDashboard.belongsTo(MainClient, { foreignKey: 'clientId' }); // Adds clientId to MainDashboard
It will link this as a foreign key and you may use it as an association. Let me know if I'm missing anything.
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 }
[...]