SQL query to filter out users who already have recieved an email - sql

Suppose:
You tried to send a mass mailing, but something went wrong, and only some users got the mail.
You sent a mass mailing recently, but now new users have signed up, and they need to receive the mail as well.
How do you filter those who have already received the news (via an Eloquent query or a select command from database .)
Actually I faced this problem in a project based on Laravel 4, I am searching for a query using Laravel Eloquent on a pivot table and keep track the sent emails to the users.
In my case there is two Model: Post and User
Its a many to many relationship: any user can receive many posts and any post can be sent to many users

You'll want to add a column to your users table eg a datetime field called mailed_at. Then in your email or signup method (wherever it is you're sending that first email) update the user with the datetime they were emailed.
From then on you can query based on mailed_at for any users who still need an email.
To check for multiple users that need a newsletter, let's say your users table schema is as follows (keeping it simple):
id | email | password | mailed_at (nullable)
We check here whether a user has received an email by querying based on the mailed_at column. To get all users that need to receive a mailout you would do the following:
$usersToMail = User::whereNull('mailed_at')->get();

There is two Model: Post and User
Its a many to many relationship: any user can receive many posts and any post can be sent to many users
I implemented it as the following:
class Post extends Eloquent {
public function recipients()
{
return $this->belongsToMany('User', 'posts_users', 'post_id', 'user_id');
}
}
class User extends Eloquent {
public function mails()
{
return $this->belongsToMany('Post', 'posts_users', 'user_id', 'post_id');
}
}
So, when I want to send a post to users I use
$usersToMail = User::whereNotExists(function($query) use ($post_id)
{
$query->select(DB::raw(1))
->from('posts_users')
->whereRaw('posts_users.user_id = users.id')
->whereRaw('posts_users.post_id = '.$post_id);
})->get();
foreach ($usersToMail as $user)
{
// send email
$user->mails()->save($post); // it records that this post has been sent to the user
}

Related

Laravel query parameters or pretty parameters for api

i am willing to know which is the best practice to use parameters for developing an api
//url
http://localhost:8000/api/my_orders/1/10/1
//route
Route::get('/my_orders/{user_id}/{limit}/{page}', 'ApiControllers\OrderController#getOrdersByUser');
//method
public function getOrdersByUser($user_id, $limit, $page)
{
//logic using $user_id, $limit, $page
}
or
//url
http://localhost:8000/api/my_orders?user_id=1&&limit=5&&page=1
//route
Route::get('/my_orders', 'ApiControllers\OrderController#getOrdersByUser');
//method
public function getOrdersByUser(Request $request)
{
//logic using $request->user_id, $request->limit, $request->page
}
this api is for mobile applications and for front end Vue application
thank you
Laravel has pagination built in, it will check for page and perpage query string arguments using paginate() or something that uses Paginator related methods for fetching a subset of your data.
So when you have ?page=1&perpage=20 and you use paginators, they will pick these up automatically.
For REST API endpoints, try and make the urls as descriptive as possible and based on your models.
// get a list of orders
GET /api/orders
// get a list of orders belonging to user 1
GET /api/orders?user_id=1
// get a paginated list of 20 orders belonging to user 1
GET /api/orders?user_id=1&page=1&perpage=20
You are calling your endpoint my_orders, which basically says it will return orders owned by the authenticated user. So, in my opinion, it wouldn't make sense here to include a user_id argument.
// get a list of orders owned by the authenticated user
GET /api/my_orders
You can use my_orders, but more descriptive will be to use a url like:
// get a list of orders owned by user
GET /api/users/{user_id}/orders
Edit:
In your case, you probably want to create a UserOrderController
public function index($user_id)
{
// first fetch user, if fetch fails show error
$user = User::findOrFail($user_id);
// maybe add some code here to check if the authenticated user is allowed to view this user's orders
// return paginated orders
return $user->orders()->paginate();
}
And you would need to define the relations in the models:
App/User.php
public function orders()
{
// assuming orders table has a column user_id
return $this->hasMany(Order::class);
}
App/Order.php
// inverse relation
public function user()
{
return $this->belongsTo(User::class);
}
I use 1st way for only those parameters which are associated with DB, like user_id here, because it reduces 1 step for me to get the specific data from DB of that particular ID. for more dynamic url laravel will go to this route even when you supply the wrong values and you have to apply the regix to avoid that.
Secondly, URLs like 2/20/4 wouldnt make any sense and hard to understand. So the best way in my opinion is the second way.

Eloquent: Get pages based on user role

I have a User, Role & Page setup, all with many-to-many relationships and the pivot tables setup in the usual fashion (role_user, page_role), along with the eloquent methods to attach a model to the pivot tables.
My idea is to allow a user to have many roles, and a page can be accessed by many roles.
However now I'd like to return a collection where I have my users details and then the pages they're allowed to access.
The closest I have got is:
return User::find( Auth::user()->id )->with('roles.pages')->first()->roles;
Now this returns each role the user has, and each page that the role can access. Which is correct, however I have duplication on the pages part.
How would I go about getting only a list of pages the user is able to access with no duplication?
Cheers
Read that answer to get you on the track: HasManyThrough with one-to-many relationship
Only for your setup you need to adjust the query - join 2 pivot tables (and make sure they represent real data, ie no rows referencing non-existing models):
// User model
// accessor so you can access it like any relation: $user->pages;
public function getPagesAttribute()
{
if ( ! array_key_exists('pages', $this->relations)) $this->loadPages();
return $this->getRelation('pages');
}
// here you load the pages and set the collection as a relation
protected function loadPages()
{
$pages = Page::join('page_role as pr', 'pr.page_id', '=', 'pages.id')
->join('role_user as ru', 'ru.role_id', '=', 'pr.role_id')
->where('ru.user_id', $this->id)
->distinct()
->get(['pages.*', 'user_id']);
$hasMany = new Illuminate\Database\Eloquent\Relations\HasMany(Page::query(), $this, 'user_id', 'id');
$hasMany->matchMany(array($this), $pages, 'pages');
return $this;
}
One more thing: I hardcoded tables and columns names for sake of simplicity, but in real life I suggest you rely on the relationships and their getters, like: $relation->getTable(), $relation->getForeignKey() etc.
Now suggestion about your code:
return User::find( // 2. query to get the same user
Auth::user()->id // 1. query to get the user and his id
)->with('roles.pages')
->first() // 3. query to get ANOTHER user (or the same, luckily..)
->roles;
Use Auth::id() instead of Auth::user()->id (for Laravel ver 4.1.25+) to avoid redundant query
find() and first() are methods that execute the query, so you just returned the user with id = Auth::user()->id and moment later you fetch another one, who comes first() from the users table..
You don't need to use User::whatever for authenticated user, use Auth::user() instead.
So the code with suggested solution would look like this:
Auth::user()->pages; // collection of Page models with unique entries

MVC4 Postal - Accessing User Email Address searching by UserId

I have a site where user A can book a lesson with a teacher, I then want to have an email sent to the teachers saying user A wants to book a lesson with you etc.
I have postal up and running sending emails without issue,
however, I don't know how to access the email address of the teacher to send the email.
The email address is saved as part of the built in UserProfile table. I have the teacher's UserId (as it's stored in a separate teacher table).
So is there a way to access the teachers email ,searching by UserId?
In any other table I would use t in db.Teacher.find(id) but this doesn't work within the Account Controller.
This was built using the default MVC4 internet website template using the built in simple membership. Let me know if more information is needed.
I've added the following to the AccountController;
private UsersContext db = new UsersContext();
public ActionResult EmailNotification(int id)
{
var user = from l in db.UserProfiles.Find(id)
select l;
}
db.UserProfiles.Find(id) however gives the following error;
Could not find an implementation of the query pattern for source type 'LessonUp.Models.UserProfile'. 'Select' not found.
Which I assume is a result of it not being created through the entity framework?
I think your query needs to be something like the following:
var result = from q in context.UserProfiles
where q.UserId == id
select q;

RavenDB - retrieving part of document

I am playing with Raven DB for few days and I would like to use it as a storage for my Web chat application. I have document which contains some user data and chat history - which is big collection chat messages.
Each time I load user document chat history is also loaded, even if I need only few fields like: user name, password and email.
My question is: how to load only part of document from database ?
Tomek,
You can't load a partial document, but you can load a projection.
session.Query<User>()
.Where(x=>x.Name == name)
.Select( x=> new { x.Name, x.Email });
That will load only the appropriate fields
From what I've seen you can do this (based on the original "User" scenario above):
public class UserSummary
{
public string Name { get; set; }
public string Email { get; set; }
}
Then you can do this:
documentSession.Query<User>().AsProjection<UserSummary>();
Looking at the Raven server it spits this out as part of the query:
?query=&pageSize=128&fetch=Name&fetch=Email&fetch=Id
So it looks like it is querying and returning only a subset of the original object, which is good.
This also works:
documentSession.Query<User>().Select( x=> new User { Name = x.Name, Email= x.Email })
But I don't think that is as clean as returning a UserSummary object.
Some follow up questions to those who have posted responses:
The link to RaccoonBlog has this example:
https://github.com/ayende/RaccoonBlog/blob/master/RaccoonBlog.Web/Infrastructure/Indexes/PostComments_CreationDate.cs
Would that method be preferred over the .AsProjection()? What is the difference between the two approaches?
Tomek, you cannot load only a part of the document.
However, I understand the problem in your case. I recommend to use two seperate documents for each user: One that actually contains the users data (name, passwordhash, email, etc.) and one that contains all the users messages. That way, it is still very cheap to load all the messages of a user and also to load a list of user for general purposes.
This is actually quite similar to how one would model a blog-domain, where you have a post and the posts comments. Take a look at RaccoonBlog to see how this works.

How do I add select parameters when reading data for the View?

I am making an MVC 3 web application in asp.net. It is supposed to be like a little community where users can send messages to each other (and later on also to specified groups of people). So in my database I have the tables Users and Messages. A row in Messages (a message) contains both sender and receiver (a foreign key to the Users table) plus some other info such as title, send date and of course the message text.
When a user come to the index page for logged in users, I want to show all messages that have been sent to this user, or sent from this user. Therefore, when reading the messages from the Messages table I want to use a parameter, the id of the logged in user (which I can access in my controller). So I have the parameter but I don't know how to use it when getting the results. I know there must be a way to do this correctly instead of putting in a bunch of sql and starting connections right in the code the ugly way.
As you maybe can see now, all the messages from the table will be retrieved and sent to the View for displaying it to the user.
The CommunityEntities line is part of the whole Entity Framework thing, not exactly sure how that stuff works yet, although I have been following some tutorials:
(Creating-an-entity-framework-data-model-for-an-asp-net-mvc-application)
(Mvc-music-store-part-1).
UserController.cs:
...
CommunityEntities db = new CommunityEntities();
public ActionResult Index()
{
var messages = db.Messages.ToList();
return View(messages);
}
...
CommunityEntities.cs:
public class CommunityEntities : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Message> Messages { get; set; }
}
EDIT: I did try that thing with db.Database.SqlQuery(...) but that seemed wrong, seeing as the point is to do this correctly and "neat". This is for my education and my teachers are going to be picky at the structure of the application.
You need a where clause:
var messages = db.Messages.Where( m => m.SentById == id || m.SentToId == id ).ToList();