test:
_attributes: { phpName: Test }
name: { type: varchar, size: 100 }
one_id: { type: INTEGER, foreignTable: second, foreignReference: id}
two_id: { type: INTEGER, foreignTable: second, foreignReference: id}
second:
_attributes: { phpName: Second }
name: { type: varchar, size: 100 }
In Doctrine I can get this with $test->getSecond1(); and $test->getSecond2(); but in Propel this doesnt work. How can I get two other fields from one relation?
To get the right associated object, when we have two foreign references to the same foreign table, we need to use:
$test->getSecondRelatedByOneId()
$test->getSecondRelatedByTwoId()
Then we get foreign references to Second in Test object with:
$test->getOneId()
$test->getTwoId()
I use only Propel at the moment, so sorry if I misunderstood.
Related
How to form a unique constraint with multiple fields in keystonejs?
const Redemption = list({
access: allowAll,
fields: {
program: relationship({ ref: 'Program', many: false }),
type: text({ label: 'Type', validation: { isRequired: true }, isIndexed: 'unique' }),
name: text({ label: 'name', validation: { isRequired: true }, isIndexed: 'unique' }),
},
//TODO: validation to check that program, type, name form a unique constraint
})
The best way I can think to do this currently is by adding another field to the list and concatenating your other values into it using a hook. This lets you enforces uniqueness across these three values (combine) at the DB-level.
The list config (and hook) might look like this:
const Redemption = list({
access: allowAll,
fields: {
program: relationship({ ref: 'Program', many: false }),
type: text({ validation: { isRequired: true } }),
name: text({ validation: { isRequired: true } }),
compoundKey: text({
isIndexed: 'unique',
ui: {
createView: { fieldMode: 'hidden' },
itemView: { fieldMode: 'read' },
listView: { fieldMode: 'hidden' },
},
graphql: { omit: ['create', 'update'] },
}),
},
hooks: {
resolveInput: async ({ item, resolvedData }) => {
const program = resolvedData.program?.connect.id || ( item ? item?.programId : 'none');
const type = resolvedData.type || item?.type;
const name = resolvedData.name || item?.name;
resolvedData.compoundKey = `${program}-${type}-${name}`;
return resolvedData;
},
}
});
Few things to note here:
I've removed the isIndexed: 'unique' config for the main three fields. If I understand the problem you're trying to solve correctly, you actually don't want these values (on their own) to be distinct.
I've also remove the label config from your example. The label defaults to the field key so, in your example, that config is redundant.
As you can see, I've added the compoundKey field to store our composite values:
The ui settings make the field appear as uneditable in the UI
The graphql settings block updates on the API too (you could do the same thing with access control but I think just omitting the field is a bit cleaner)
And of course the unique index, which will be enforced by the DB
I've used a resolveInput hook as it lets you modify data before it's saved. To account for both create and update operations we need to consult both the resolvedData and item arguments - resolvedData gives us new/updated values (but undefined for any fields not being updated) and item give us the existing values in the DB. By combining values from both we can build the correct compound key each time and add it to the returned object.
And it works! When creating a redemption we'll be prompted for the 3 main fields (the compound key is hidden):
And the compound key is correctly set from the values entered:
Editing any of the values also updates the compound key:
Note that the compound key field is read-only for clarity.
And if we check the resultant DB structure, we can see our unique constraint being enforced:
CREATE TABLE "Redemption" (
id text PRIMARY KEY,
program text REFERENCES "Program"(id) ON DELETE SET NULL ON UPDATE CASCADE,
type text NOT NULL DEFAULT ''::text,
name text NOT NULL DEFAULT ''::text,
"compoundKey" text NOT NULL DEFAULT ''::text
);
CREATE UNIQUE INDEX "Redemption_pkey" ON "Redemption"(id text_ops);
CREATE INDEX "Redemption_program_idx" ON "Redemption"(program text_ops);
CREATE UNIQUE INDEX "Redemption_compoundKey_key" ON "Redemption"("compoundKey" text_ops);
Attempting to violate the constraint will produce an error:
If you wanted to customise this behaviour you could implement a validateInput hook and return a custom ValidationFailureError message.
I've got a simple schema:
export default {
title: 'hash schema',
version: 0,
primaryKey: 'hash',
type: 'object',
keyCompression: true,
properties: {
uuid: { type: 'string' },
id: { type: 'number' }
}
}
I want to have a table with a string field uuid as a primary key and I want to map it to an unique number that is automatically increased.
Is there a way to do so?
"automatically incrementing" doesn't sound very UUIDish to me. UUIDs are supposed to be random and unpredictable. You leave yourself vulnerable to the German Tank Problem
Nevertheless, you can indicate a string should be a UUID in JSON Schema by using "format": "uuid". It is only available in implementations supporting specification version draft2019-09 or later.
In PHP Lighthouse you can have ManyToMany relationships. Using nested operations allows you to create say an Author, Post and connect them using a pivot table post_author ALL in one operation.
Lighthouse also allows you to store data in the pivot table. In their docs, they give an example of how to connect a record with some pivot table data. There is no example available on how to do a create operation with extra pivot table data.
Docs pivot data update operation: https://lighthouse-php.com/master/eloquent/nested-mutations.html#storing-pivot-data
type Mutation {
createPost(input: CreatePostInput! #spread): Post #create
}
input CreatePostInput {
title: String!
authors: CreateAuthorBelongsToMany
}
input CreateAuthorBelongsToMany {
create: [CreateAuthorInput!]
}
input CreateAuthorInput {
name: String!
#contribution_percentage: Int! #Pivot table column
}
#query:
mutation {
createPost(
input: {
title: "My new Post"
authors: {
create: [{ name: "Herbert", contribution_percentage: 50 }]
}
}
) {
id
authors {
name
}
}
}
I tried fiddling with the scheme to "guess" the correct scheme, to no avail.
My objective is: Create an Author, Post, and connect them using a post_author pivot table WITH extra pivot data, ALL in one operation.
I have some Laravel models that are related via a pivot table in a belongsToMany relation.
Now I try to create a mutation in Laravel Lighthouse to join the models and also fill the pivot values.
Somehow I cannot find out how to do this.
The Course model looks like this:
class Course extends Model
{
public function programs(): BelongsToMany
{
return $this->belongsToMany(\App\Models\Program::class, 'Course_Program')
->withPivot('id', 'year_id')
->withTimestamps();
}
}
My GraphQL code looks like this:
type Mutation {
createCourse(input: CreateCourseInput! #spread): Course! #create
}
type Course {
id: ID!
name: String!
programs: [ProgramWithPivot!]! #belongsToMany
}
type Program {
id: ID!
name: String!
}
type ProgramWithPivot {
id: ID!
name: String!
year_id: ID!
}
input CreateCourseInput {
name: String!
programs: CreateCourseProgramsRelation!
}
input CreateCourseProgramsRelation {
create: [CreateCourseProgramInput!]
}
input CreateCourseProgramInput {
id: ID!
year_id: ID!
}
The problem is that when I try to create programs in my course like this:
mutation {
createCourse(input: {
name: "new cours"
programs: {
create: [{
id: 1
year_id: 2
}]
}
}) {
id
programs {
id
year_id
}
}
}
Laravel Lighthouse tries to insert data into the Program table (and complains that Program.name does not have a default value).
However, I want to insert data (course_id, year_id and program_id) into the Course_Program table.
How do I tell Laravel Lighthouse to insert data in the pivot-table?
Mutating pivot data is currently not there, but I did a PR for this last week. You can follow the PR progress
In general it will be included either the way I did it, or maybe a little bit different. It was discussed in this issue
For now you can update your pivot data only using custom resolver/directive. But probably the best way will be just to wait till PR gets merged.
There is any way of doing update / upsert to pivot data via nested mutation?
I have a bit of problem for using Sequelize with include. The problem is that my model uses two primary keys in child table.
So it goes like this
Parent table
User : Id, ...
Post : Id, UserId(foreign key, binds to user id), ...
Post Hash Tag : HashTag, PostId(foreign key, binds to Post id), UserId(foreign key, binds to user id of Post table)
So the table hierarchy looks like this
user - post - post hash tag
Now when I try to do like this,
Post.findAll(
include: {
model: post hash tag
}
)
then it only searches the post hash tags for where post id of post hash tag table is equal to post id of post table
So I added like this
Post.findAll(
include: {
model: post hash tag
where: {
col1: models.sequelize.where(models.sequelize.col('POST.USER_ID'), '=', models.sequelize.col('POST_HASH_TAG.USER_ID'))
}
}
);
Then it will gives a problem at 'where' clause that Post.USER_ID cannot be found.
If I change col1 value to Post.userId then now it solves the above error but gives another error at 'on' clause
Do you have any idea how I can solve this?
The full model is given here
User
sequelize.define('User', {
id: { type: DataTypes.STRING(6), field: 'ID', primaryKey : true }
)
Post - I know multiple primary declaration is not working correctly, so don't bother to consider too much
sequelize.define('Post', {
id: { type: DataTypes.STRING(6), field: 'ID', primaryKey: true },
userId: { type: DataTypes.STRING(6), field: 'USER_ID', primaryKey: true }
)
Post hash tag
sequelize.define('PostHashTag', {
postId: { type: DataTypes.STRING(6), field: 'POST_ID', primaryKey: true },
hashTag: { type: DataTypes.STRING(20), field: 'HASH_TAG', primaryKey: true },
userId: { type: DataTypes.STRING(6), field: 'USER_ID', primaryKey: true }
}
)
and the query I used is
Post.findAll({
attributes: ['id', 'userId'],
where: {
userId: userId,
id: { $lt: postId }
},
include: [{
model: models.PostHashTag,
attributes: ['hashTag'],
where: {
col1: models.sequelize.where(models.sequelize.col('Post.USER_ID'), '=', models.sequelize.col('PostHashTag.userId'))
}]).then(...)
I found an answer by myself... col1:
models.sequelize.where(models.sequelize.col('Post.USER_ID'), '=', models.sequelize.col('PostHashTag.userId'))
this should be
userId: models.sequelize.where(models.sequelize.col('POST.userId'), '=', models.sequelize.col('POST_HASH_TAG.USER_ID'))
this will work. The physical names of table and column used in parenthesis