Sanity in React: How can I fetch many students related to one project??? (Many authors to 1 Block) - sanity

I am working on a portfolio website in React using Sanity. The two components objects are students and projects. On the homepage, I can click on a link to a specific project, and this routes to another page showing the individual project. Furthermore, on this single page I would like to display all the students who have worked on this project. With sanity studio, I have created the necessary relations.
However, no matter on which project I click, it displays all students, no matter if they are related or not. I suspect that I am doing something wrong in my Sanity.fetch. This is from my SingleProject component where I want data about the project and the related students(One to many)
.fetch(
`*[slug.current == "${slug}"] {
title,
_id,
slug,
mainImage{
asset->{
_id,
url
}
},
body,
description,
tags,
"students": *[_type == "student" && project._ref in *[_type=="project" && title == title ]._id ]{
name,
mainImage{
asset->{
id,
url
}
},
"slug": slug.current,
}
}`
This is from my project schema referencing the students from the student schema.
export default {
name: 'project',
title: 'Project',
type: 'document',
fields: [
{
title: 'Students',
name: 'students',
type: 'array',
of: [
{
type: 'reference',
to: [{ type: 'student' }],
},
],
},
I don't know why it fetches every student in the project component. Can somebody help?

After long research, I have found the answer.
useEffect(() => {
sanityClient
.fetch(
`*[slug.current == "${slug}"] {
title,
_id,
slug,
mainImage{
asset->{
_id,
url
}
},
body,
description,
tags,
"projects": *[_type=='project' && references(^._id)]{
title,
mainImage{
asset->{
id,
url
}
}
},
"slug": slug.current
}`
)
.then((data) => setSingleStudent(data[0]))
.catch(console.error);
}, [slug]);`enter code here`

Related

Resolve reference in Block Content in Sanity.io

I have the Content Block in Sanity like so:
export default {
title: "Block Content",
name: "blockContent",
type: "array",
of: [
/// stuff here
{
title: "Book",
type: "reference",
to: [{ type: "book" }],
},
],
};
When making a query like
'*[_type == "post"]{...,body[]{..., asset->{..., "_key": _id}, markDefs[]{..., _type == "internaLink" => {"slug": #.reference->slug}}}';
I get the reference, but I want to return the full Document. I tried every method but the documentation only explain references outside Content Blocks and those methods aren't working.
This is what's returned from the Query:
_createdAt: '2020-12-07T14:43:34Z',
_id: '9d628aa6-aba7-4b53-aa9f-c6e97583baf9',
_rev: 'ZZ0GkIKCRvD0tdMQPywPfl',
_type: 'post',
_updatedAt: '2020-12-07T14:43:34Z',
body: [
{
_key: '4184df372bae',
_type: 'block',
children: [Array],
markDefs: [],
style: 'normal'
},
{
_key: '56bed8835a7d',
_ref: 'dc2eefee-2200-43e1-99c7-ea989dda16ba',
_type: 'reference'
}
],
title: 'Example'
Solved, I'm writing the solution here as I found nothing on the web.
Basically I tried anything randomly until I got the desired result.
The query is:
_type=="reference"=>^->
I had a similar situation where I had was grabbing a story object that had a body which was a blockContent, and I had just added a blog reference as a block option to the body.
I solved it by using this query:
const query = groq`*[_type == "story" && slug.current == $slug][0]{
title,
description,
body[]{
_type == 'blog' => #->,
_type != 'blog' => #,
}
}`
It loops through all the array items inside of the body and if a specific one is a blog, then it dereferences it by using ->, otherwise it outputs the already existing value.

How to search for/select by included entity but include all related entities into result set

In my application, I am using sequelize ORM. There are several entities: A Tool can have Tags and Categories.
Now I want to search for all Tools, that have a specific Tag, but I want to include all relating Tags of that tool (not just the specific one). If I now place a where statement into the include, only specified Tags are included into the result set (see [2]). I tried to limit the Tags in the outer where statement (see [1]), but this does not help either.
Example
Tool A has Tags t1, t2 and t3. Now I want to search all Tools that have the Tag t3, but the result set shall contain all three tags.
Expected result:
Tool A
\
- Tag t1
- Tag t2
- Tag t3
db.Tool.scope('published').findAll({
where: { '$tool.tag.name$': filter.tag }, // [1] Does not work
include: [
{
model: db.User,
attributes: ['id', 'username']
},
{
model: db.Tag,
attributes: ['name'],
through: { attributes: [] },
// [2] Would limit the result specified tag
// where: {
// name: {
// [Op.and]: filter.tag
// }
// }
},
{
model: db.Category,
attributes: ['id', 'name', 'views'],
through: { attributes: ['relevance'] },
where: {
id: {
[Op.and]: filter.category
}
}
}
],
where: {
title: {
[Op.like]: `%${filter.term}%`,
}
},
attributes: ['id', 'title', 'description', 'slug', 'docLink', 'vendor', 'vendorLink', 'views', 'status', 'createdAt'],
order: [['title', 'ASC'], [db.Tag, 'name', 'ASC']]
})
I know I could perform this by performing a select via the Tag in the first place (db.Tag.findAll() instead of db.Tool.findAll(); I've already done this elsewhere in my project), but at the same time I also want to be able to filter by another entity (Category) the same way. So the Tool.findAll() should be the starting point.
Any help appreciated!
First off, you have two where clauses in your top-level query:
where: { '$tool.tag.name$': filter.tag }, // [1] Does not work
// ...
where: {
title: {
[Op.like]: `%${filter.term}%`,
}
},
I think your best approach is going to be with a literal subquery in the WHERE clause. Basically we want to find the ids of all of the tools that have the right tag and that contain the filter.term.
The subquery part for the WHERE looks something like...
SELECT ToolId FROM ToolTags WHERE TagId='t2';
Inspired by the subquery solution from this post Sequelize - subquery in where clause
// assuming your join table is named 'ToolTags' in the database--we need the real table name not the model name
const tempSQL = sequelize.dialect.QueryGenerator.selectQuery('ToolTags',{
attributes: ['ToolId'],
where: {
TagId: filter.tag
}})
.slice(0,-1); // to remove the ';' from the end of the SQL
db.Tool.scope('published').findAll({
where: {
title: {
[Op.like]: `%${filter.term}%`,
},
id: {
[Op.In]: sequelize.literal(`(${tempSQL})`)
}
},
include: [
{
model: db.User,
attributes: ['id', 'username']
},
{
model: db.Tag,
attributes: ['name'],
through: { attributes: [] },
},
// {
// model: db.Category,
// attributes: ['id', 'name', 'views'],
// through: { attributes: ['relevance'] },
// where: {
// id: {
// [Op.and]: filter.category
// }
// }
// }
],
attributes: ['id', 'title', 'description', 'slug', 'docLink', 'vendor', 'vendorLink', 'views', 'status', 'createdAt'],
order: [['title', 'ASC'], [db.Tag, 'name', 'ASC']]
})
I commented out your category join for now. I think you should try to isolate the solution for the tags before adding more onto the query.

Is it possible to show/hide fields in the KeystoneJS 5 AdminUI?

Basically what the title says -- we are working on a project where we'd like to be able to show and hide various fields based on the value of other fields. This seems to have been possible in KeystoneJS 4 but I see no mention of it in KeystoneJS 5.
dependsOn feature of keystoneJs v4 has not made it to latest KeystoneJs iteration. v5 (as we call it) is complete rewrite and does not have many features from v4.
however there is a Pull Request which may add this feature but unfortunately that is not the priority for the core team and they have not responded on the PR.
once that PR is merged you can do something like this
keystone.createList('Test field', {
fields: {
price: { type: Decimal, symbol: '$' },
currency: { type: Text, dependsOn: { $lt: { price: 3 } } },
hero: { type: File, adapter: fileAdapter, dependsOn: { $gt: { price: 3 } } },
markdownValue: { type: Markdown, dependsOn: { $gt: { price: 6 } } },
fortyTwo: {
type: Virtual,
graphQLReturnType: `Int`,
resolver: () => 42,
},
}});

Select from multiple tables in sequelize

I'm struggling in how to select from two tables using the Sequelize.
Actually I'm trying to do it:
SELECT * FROM users, clients WHERE user.id = clients.user_id
I have no idea how to user two tables as I described, the only thing I did that got some results were:
const clients = await Client.findAll({
attributes: ["user_id"],
});
const users = [];
for (const client of clients) {
let user = await User.findAll({
where: {
id: {
[Op.eq]: client.user_id
}
}
});
users.push(user);
}
Which return me something:
[
[
{
"id": 1,
"first_name": "Velda",
"middle_name": "Zboncak",
"last_name": "Kris",
"email": "vkris10#hotmail.com",
"created_at": "2020-02-07T20:09:29.484Z",
"updated_at": "2020-02-07T20:09:29.484Z"
}
]
];
Model and Assossiation
First of all, you need to create the correct associations in the model of your table. In this case for the User and the Client, it's supposed to be an Client.belongsTo(...)
Take a look at User model:
const { Model, DataTypes } = require("sequelize");
class User extends Model {
static init(sequelize) {
super.init({
first_name: DataTypes.STRING,
middle_name: DataTypes.STRING,
last_name: DataTypes.STRING,
email: DataTypes.STRING
}, { sequelize });
}
}
module.exports = User;
Take a look at Client model:
const { Model, DataTypes } = require("sequelize");
class Client extends Model {
static init(sequelize) {
super.init({
user_id: DataTypes.INTEGER // The foreign key
}, { sequelize });
}
static associate(models) {
Client.belongsTo(models.User, {
foreignKey: "id", // Column name of associated table
as: "user" // Alias for the table
});
}
}
module.exports = Client;
When associating tables you need to have in mind those values inside the associate method, being the foreignKey: "id" the column name inside the models.ModelName, which will be used to make the joins, and the as: "user" which are used as an alias for the table like SELECT t.column1 FROM table AS t;
Controller
Okay, now you have set your models, you need to set your controller, where the magic happens. As you said you want to fetch results using:
SELECT * FROM users, clients WHERE user.id = clients.user_id
But to achieve the same result you can follow the sql join method to fetch the results from db, so it will be something like this:
SELECT
"user"."first_name", "user"."middle_name", "user"."last_name", "user"."email"
FROM "clients" AS "client"
LEFT JOIN "users" AS "user"
ON "client"."id" = "user"."id";
Knowing that we can talk about including tables in sequelize, which is the same as associations
const Client = require("./path/to/models/Client");
module.exports = {
async fetchAll(req, res) {
const results = await Client.findAll({
limit: 25,
include: [
{
association: "user",
attributes: ["first_name", "middle_name", "last_name", "email"]
}
]
});
return res.json(results);
},
};
Now lets talk about what is going on in the code:
The Model.findAll({}) will fetch all the result inside the specified table, in this case clients table.
The limit: 25 will limit your results in only 25 rows, you are free to remove or edit as you need.
The include: [], it will do the joins through the tables you specify, as you need only the users table, we are going to use only one object, so the assossiation: "user" will make this connection between tables, you must use the same alias you set inside the model. And at least the attributes: ["columns"] is where you set all the fields you want to fetch.
And that's it, you make you request, and the result of this will be exactly the same join as I mentioned. And the results will be:
[
{
"id": 1,
"user_id": 1,
"user": {
"first_name": "John",
"middle_name": "Ironsight",
"last_name": "Doe",
"email": "johndoe#example.com"
}
}, {...}
]
Can use where in include. Find the document at here
let user_id = client.user_id;
users = await User.findAll({
include: [
{
model: Client,
as: 'client',
where: {
user_id: user_id
}
}
]
});

Paging and grouping in dynamic grid

I am using dynamic grid using this plugin.
I want to make paging in it,
I tried like,
Ext.define('....', {
extend: 'Ext.data.Store',
pageSize: 10,
proxy: {
type: 'rest',
url: me.url,
reader: {
type: 'dynamicReader',
totalProperty: 'totalCount'
}
}
});
me.bbar = Ext.create('Ext.PagingToolbar', {
store: me.store,
displayInfo: true,
displayMsg: 'Displaying topics {0} - {1} of {2}',
emptyMsg: "No topics to display"
});
In DynamicGrid.js totalProperty is not working. Am I setting the property properly there?
Then I am also trying to make grouping in the same plugin.
I have a combobox with some fields and want to select grouping field from it dynamically. When I select a field in combo box, it sends that data to grid's groupField property.
I have that combo box value selected in controller like,
var groupData = Ext.ComponentQuery.query('#groupid')[0].getValue();
I am sending it to grid like,
Ext.define('Group', {
singleton: true,
param: groupData
});
I am getting that for grid property (in DynamicGrid.js) like,
groupField: [Group.param]
But this automatically selects first field for groupField property before even selecting anything in combo box and makes grouping, selecting other fields in combo box also doesn't work, it always has first field for grouping.
What is going wrong? Please help.
I did grouping successfully by adding the following code in listener,
me.store.group(Group.param);
Still having issues with totalProperty, can someone help me to make it work?
I think I am making a mistake, now actual JSON response is,
[{
"userId": 123,
"name": "Ed Spencer",
"email": "ed#sencha.com"
}]
So the code for getting data and manipulating works fine like,
readRecords: function(data) {
if (data.length > 0) {
var item = data[0];
var fields = new Array();
var columns = new Array();
var p;
for (p in item) {
if (p && p != undefined) {
fields.push({name: p, type: 'floatOrString'});
columns.push({text: p, dataIndex: p});
}
}
data.metaData = { fields: fields, columns: columns };
}
return this.callParent([data]);
}
But for sending other metaData properties, from the docs I should probably have the following JSON response,
{
"count": 1,
"ok": true,
"msg": "Users found",
"users": [{
"userId": 123,
"name": "Ed Spencer",
"email": "ed#sencha.com"
}],
"metaData": {
"root": "users",
"idProperty": 'userId',
"totalProperty": 'count',
"successProperty": 'ok',
"messageProperty": 'msg'
}
}
So how do I point root in the readReords function so that it knows data is in root?
Thereby I will have other metaData properties also passed.
Please help!