I understand that this question may have more than one DBMS-related answer, but maybe not, so here it goes.
So, I need my app to asynchronously grab some data from the backend, say something like a User and his Posts. I am using Vue2 with Axios on the frontend, so that it would be nice to write something like:
mounted: function() {
axios.get('/users')
.then((response) => {
this.users = response.data;
})
.catch(function (err) {
// Handle this
});
}
The outcome I'd like to have is the users object in my VueVM's data to contain also an array of posts for each User. Currently I build a custom associative array in my backend-side controller as follows:
$posts = Post::where('user_id', $id)->get();
return json_encode(['user' => $user, 'posts' => $posts]);
Or, when feasible, I use the has_many method, which is also pretty handy, but I find this solution pretty ugly and lacking reusability.
With Mongoose and MongoDB I would use the populate() method, but I cannot find (that is I actually don't know how to search for it) an equivalent to this with Eloquent.
Any suggestion?
If you have defined a posts() relationship in your User model, when retrieving the users, you can eager load the related data:
$users = User::with('posts')->get();
or a specific User:
$user = User::with('posts')->find($id);
Then $user->posts would contain a collection of the related Post models.
I suggest you read through https://laravel.com/docs/5.5/eloquent-relationships
Related
Let's say I have 2 models.
Post
Author
The Author looks like below:
{
authorId: 'ghr4334t',
fullName: 'This is a post!'
Nickname: 'Avola
}
The Post looks like below and will have a reference to the author like below:
{
postId: '12fdc24',
authorId: 'ghr4334t',
content: 'This is a post!'
}
Currently, when a user clicks on a post, in order to show all the relevant information, I load the data as follow:
getPost(postId).then(post=> {
getAuthor(listing.uid).then((document) => {
// update state so I have the post object and author object.
})
})
So the above, I load the post, then I load the author. Once I've loaded them both, I can finally construct a custom object:
const finalPost = {
author: { ...this.state.authorData },
post: { ...this.state.postData }
}
Naturally..If I have a couple more fields that reference other collections, there will be a nest of get and .then() calls like below:
getPost(postId).then(post=> {
getAuthor(listing.uid).then((document) => {
getSomethingElse(listing.uid).then((document) => {
getAnother(listing.uid).then((document) => {
// finally update state with everything.
})
})
})
})
Is there a more a better way to load related information together without having to stack .then() calls?
Unfortunately, there isn't a better way to achieve what you want, with queries directly. Queries in Firestore doesn't provide you with many options on how to query and return data, mainly, when you would need to do any kind of JOIN on them, to search via references, which makes the work not very easy. I believe the way you are doing is the best option you have for more now.
An alternative you can try is to have Subcollections, where you will have a subcollection of Author inside your collection Post. This way, you will only treat with the reference of the Post, since the Author will be within the document of each specific Post. This way, the queries would be more simple, looking like this below. Of course, this would require you to modify your database.
var messageRef = db.collection('Post').doc('Post1')
.collection('Author').doc('Author1');
In case you still think this is not enough, I would recommend you to raise a Feature Request at Google's System, where the Google Developers will be able to check if having a new way of getting data is possible to be implemented.
Let me know if the information clarified your doubts!
I want to delete from an articles table using knex by article_id. This already exists in comments table as a foreign key.
How can I test that data has been deleted and how can I send that to the user.
I decided to approach this by writing a function to delete from both functions with a .then. Does this look like I am on the right lines?
exports.deleteArticleById = function (req, res, next) {
const { article_id } = req.params;
return connection('comments')
.where('comments.article_id', article_id)
.del()
.returning('*')
.then((deleted) => {
console.log(deleted);
return connection('articles')
.where('articles.article_id', article_id)
.del()
.returning('*');
})
.then((article) => {
console.log(article);
return res.status(204).send('article deleted');
})
.catch(err => next(err));
};
At the moment I am getting the correct data with the logs but I am getting a status 500 but I think I need to be trying to get a 204?
Any help would be much appreciated.
What you're trying to do is called a cascading deletion.
These are better (and almost always) handled at the database level instead of the application level.
It's the job of the DBMS to enforce this kind of referential integrity assuming you define your schema correctly so that entities are correctly linked together, via foreign keys.
In short, you should define your database schema as such that when you delete an Article, it's associated Comments also get deleted for you.
Here's how I would do it using knex.js migrations:
// Define Article.
db.schema.createTableIfNotExists('article', t => {
t.increments('article_id').primary()
t.text('content')
})
// Define Comment.
// Each Comment is associated with an Article (1 - many).
db.schema.createTableIfNotExists('comment', t => {
t.increments('comment_id').primary() // Add an autoincrement primary key (PK).
t.integer('article_id').unsigned() // Add a foreign key (FK)...
.references('article.article_id') // ...which references Article PK.
.onUpdate('CASCADE') // If Article PK is changed, update FK as well.
.onDelete('CASCADE') // If Article is deleted, delete Comment as well.
t.text('content')
})
So when you run this to delete an Article:
await db('article').where({ article_id: 1 }).del()
All Comments associated with that Article also get deleted, automatically.
Don't try to perform cascading deletions yourself by writing application code. The DBMS is specifically designed with intricate mechanisms to ensure that deletions always happen in a consistent manner; It's purpose is to handle these operations for you. it would be wasteful, complicated and quite error-prone to attempt to replicate this functionality yourself.
i'm trying to create a post-comment relationship​​ where the a user can write a post and others users can comment on the post.
I can show the posts but when in trying to do the join for displaying the comments that belongs to the post i cant..
below is my db schema
i was thinking that first i need to get the key from the posts node and then move to comments and somehow get the comments of each post..
and use it in *ngfor inside the ngfor of the post?
i was trying something like
findAllComments(){
this.db.list('posts', { preserveSnapshot: true})
.subscribe(snapshots=>{
snapshots.forEach(snapshot => {
return this.db.list(`comments/${snapshot.key}`)
});
});
}
but this returns void of course:
When I console.log:
findAllComments(){
this.db.list('/posts', { preserveSnapshot: true})
.subscribe(snapshots=>{
snapshots.forEach(snapshot => {
const kapa = this.db.list(`comments/${snapshot.key}`).do(console.log)
kapa.subscribe();
});
});
}
I get in console this
I'm not sure if my thinking on this is right.
I'm confused because I am new in angular and firebase.
You aren't returning a subset of posts (you're querying on all posts) so there's no need to have a join of any sort here. You can just query for all comments:
findAllComments(){
// {preserveSnapshot: true} is deprecated
return this.db.list('/comments').snapshotChanges();
}
Assuming you actually want to retrieve a subset of comments (not what your example depicts), you could do something like this:
this.replies = db.list('AngularFire/joins/messages').snapshotChanges().map(snapshots => {
console.log('snapshots', snapshots);
return snapshots.map(ss => {
return db.list(`AngularFire/joins/replies/${ss.key}`).valueChanges();
});
});
There is a complete working example of the latter here.
I guess in the first part, you are not subscribing to the comments list. As there is no subscription to the comments, the request to the get the list of comments from firebase will not be fired and hence you don't see any comments.
In the second part, as you are subscribing to the comments list, you are seeing them.
In cases like these, where you want to fetch something based on a previous request, you could use switch/concat/merge Maps. Hope this helps
Fairly simple problem, just cant find the good/clean way to do this without making a call to another find
I've got my node app rigged up with Angular-Resource, and I'm just making some round-trip like data calls on new or changed data.
So ngResource making the $save() call to my /api/users/:id and such. And Node reacts to this call by creating or finding the user, making the updates, and saving them.
Whether through create() or save(), it returns the created record, and for right now, I use res.json(user) to spill the created/returned record for my Angular to handle populating my view with the updated information
Now, I know with Sequelizes find() and findAll() methods, I can use findAll({ include: [{ all: true }]}) or specify my models individually.
What I want to know is, what is the best way to get my records associations on save/create
and unfortunately, this just doesn't work:
models.User.create(newuser, {include:[{ all: true }]}).then(function(user) {
res.json(user);
});
Do I really have to perform another find() just to get my managed models associations?
To better illustrate the opted solution from RedactedProfile's comment, here's the code.
models.User
.create(newuser, {include:[{ all: true }]})
.then(user => {
user.reload().then(user => { res.json(user); })
});
I have a way that works, it just seems like a stupid way of doing things.
I have an Invoice object ($resource) that has a client value, which, because of the way Mongoose works, is assigned to an ID. But, I want to use the client values (e.g. client.name, client.address, etc.) in the invoice views in Angular. Here's the code I have, which takes invoice.client (an ID) and reassigns the whole client object:
Invoices.query(function(invoices) {
angular.forEach(invoices, function(invoice){
Clients.get({
clientId: invoice.client
}, function(client) {
invoice.client = client;
})
});
$scope.invoices = invoices;
});
This seems very redundant, because I already have all the clients loaded in $scope.clients, but I couldn't think of a way to use those instead, which would require far fewer database calls.
$scope.clients is a promise object, since it is populated like this:
Clients.query(function(clients) {
$scope.clients = clients;
});