Codeigniter Noob Question--active record and results? - mysql

I'm writing a simple app. I need to block user from a page if their credit is < 0.
I have a table "User_profiles" with a "credit" row.
How can I set up a model in conjunction with the controller to send the user to another page if the value of "credit" is 0?
This should be straightforward, but I'm new at the select->where stuff...
It has to be the row of the current user too--I don't know how to traverse arrays very well yet.
Thanks!

Well, the easiest solution would be to just load a different view...
As for the model, it would look like this:
class UserModel extends Model {
public function getUserCredit($id) {
$this->load->database();
//effectively generates: SELECT `credit` FROM `User_profiles` WHERE `id`=$id
$query = this->db->select('credit')->where('id',$id)->get('User_profiles');
//row() executes the query for a single result, returns the credit property
return $query->row()->credit;
}
}
Then in the controller:
class Users extends Controller {
//....
public function credit() {
$this->load->model('userModel','users');
// assuming the session library has been loaded
$user_id = $this->session->userdata('id');
$credit = $this->users->getUserCredit($user_id);
if ($credit == '0') $this->load->view('users/no_credit');
else $this->load->view('users/credit');
}
}
That's untested, but it should at least help you get the idea.
When you request the page /users/credit/1, CI will call the Users::credit(1) action.
It then loads UserModel as $this->users
You call $this->users->getUserCredit(1), which translates to UserModel::getUserCredit(1), to store as $credit
The model loads the database.
You tell the db to select('credit') (select the credit column), where('id',1) (where the id = 1), then get('User_profiles') (get matching rows from the User_profiles table). That returns a query, which you store as $query for readability.
getUserCredit returns the credit property of the single-row result of the query
If $credit == 0, you load the view views/users/no_credit.php
Otherwise, you load the view views/users/credit.php (it's conventional to name the views after the actions they represent and put them in a folder corresponding to the controller)

Related

Symfony 3.4 : how to log the history of user actions?

I want to store in my database all the user actions done about an entity.
For example, for 1 entity, I want to store :
Created by (= author)
Updated by
Date of creation
Date of update
I want to store the history of the actions of a user, not the last ones. I thought I could create a table with these columns :
log_id
user_id
entity_id
action (= "create" or "update" or something else)
date
And then, I could easily get the last update of my entity and display the date and the user who did it.
Is there a Symfony bundle to do this ? Should I use Monolog ?
I will do this for many entities and I'm not sure if this is the correct way to do...
Is it possible to create only one logs table to store each log about each entity ? It bothers me to create 1 logs table per entity.
Since Doctrine is event based, it's easy:
Either use an extension, like Gedmo Loggable
Or hook into Doctrine's events and log, using Monolog, everything that happens in your app.
Personally I would prefer option 2 since I'm a control maniac, it's a little more complex though. Personally I would also use Monolog so I could abstract away the way how and where the log entries are stored.
When you decide how to approach this and you will need any assistance along the way, please ask another question.
Good luck.
I don't know if that would fit what you need, but you could easily add a Listener to the symfony kernel to log every controller used.
Something like this :
class UserLogListener {
protected $authChecker;
protected $tokenStorage;
protected $entityManager;
public function __construct(TokenStorageInterface $tokenStorage, AuthorizationChecker $authChecker, EntityManager $entityManager)
{
$this->authChecker = $authChecker;
$this->tokenStorage = $tokenStorage;
$this->entityManager = $entityManager;
}
public function onKernelRequest(GetResponseEvent $event)
{
if( $this->tokenStorage->getToken() != null){
$user = $this->tokenStorage->getToken()->getUser();
$currentDate = new \Datetime();
$action = $event->getRequest()->attributes->get('_controller');
$method = $event->getRequest()->getMethod();
$userIp = $event->getRequest()->getClientIp();
$userLogRepository = $this->entityManager->getRepository(UserLog::class);
if($user instanceof User){
$userLog = new UserLog();
$userLog->setUser($user);
$userLog->setIp($userIp);
$userLog->setAction($action);
$userLog->setMethode($method);
$userLog->setDate($currentDate);
if($event->getRequest()->request && $methode=='POST'){
$userLog->setData(json_encode($event->getRequest()->request->all()));
}else{
$userLog->setData($event->getRequest()->getPathInfo());
}
$this->entityManager->persist($userLog);
$this->entityManager->flush();
}
}
}
}
What it does is add to the database (with an entity called UserLog) information about every page called. So you can know which action is made by knowing which controller is called, and you can also log the request data so you can find out what modification/creation the user did.

Eloquent query problem using with() function for model relationship eager loading

How do write this eloquent query in Laravel so that it eager loads with() the relationship model in this example between a User model and Profile model? I was trying to avoid 2 separate queries.
I feel I am close, but somethings not quite right.
$author = User::where('id', $id)->with('profile')->get();
The collection is returning the user details correctly. But it's showing the profile relationship as null.
#relations: array:1 [▼
"profile" => null
]
I believe I have things setup correctly with a User model and a Profile needed relationships.
User.php
public function profile()
{
return $this->hasOne('App\AuthorProfile', 'user_id');
}
AuthorProfile.php
public function user()
{
return $this->belongsTo('App\User');
}
Assuming for AuthorProfile model table you have record with id of user it should be fine.
However you wrote:
I was trying to avoid 2 separate queries.
Well, it's not true, if you have single record, eager loading won't help you at all. In this case 2 queries will be execute - no matter if you use eager loading or you won't.
Eager loading would help if you had multiple users and for each of them you wanted to load profile, but if you have single record it won't change anything.
Additionally instead of:
$author = User::where('id', $id)->with('profile')->get();
you should rather use:
$author = User::with('profile')->find($id);
because you expect here single user.
$users = User::with('profile')->find($id);
Your model should be like this.The User_id on the profile table and id on the user table
public function profile()
{
return $this->hasOne('App\AuthorProfile', 'user_id','id');
}

yii2 getting data from two models and presenting it in view

I have two models users and pictures
To draw all pictures in view I have to get string from pictures, one of data is userID, then get string from users according to that userID, so I get userFolder
Then I can draw picture using combined data.
And this must be done for all pictures in DB.
I can start from controller
$picturesModel= Pictures::find()->all();
But then I have to do what? run a loop while which get users data, and then get complete data for drawing a picture and store it in some new array which after loop finish I should pass to view? Is this the best way ? or there is anything simple ?
Assuming that your Pictures has a relation one to one with Users
the you can add to you Pictures model a function
class Pictures extends ActiveRecord
{
// ...
public function getUser()
{
return $this->hasOne(Users::className(), ['id' => 'user_id']);
}
}
then if you controller or in your view you need accessing to the user related to the picture you can
$pictureModel = Pictures::find()->where(['id'=>123])->one();
You can access to the user related
$userModel= $pictureModel->User;
or for a collection of pictures
$picturesModels= Pictures::find()->all();
$userModel = $picturesModels[0]->user
or for the loop
$picturesModels= Pictures::find()->all();
foreach( $picturesModels as $key => $value ) {
echo $value->user->your_att;
}
you can take a look at http://www.yiiframework.com/doc-2.0/guide-db-active-record.html and http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#relational-data

Add WHERE condition to all SQL requests in Laravel

I'm creating an online tool for companies that each have a set of users in Laravel.
When a user is connected, he has a $connected_company_id variable
For every SELECT request (called by ::all(), find(), ...), i would like to add the condition: where company_id = $connected_company_id. I have found this post: laravel set an automatic where clause, but it doesn't work by overriding newQuery().
For every INSERT request, i would like to add the company_id.
Is this possible without changing my code inside all the controllers ?
I thought about extending Eloquent with customEloquent, and then make my models extend customEloquent, but I don't know how to write the code for customEloquent and if it could work.
Well, you could make use of the Eloquent Model Events. I assume you have the connected_company_id stored in the Session company_id
class BaseModel extends Eloquent{
public static function boot(){
parent::boot();
//Column to inject when inserting
static::creating(function ($obj){
$obj->company_id = Session::get('company_id');
});
//Column to inject when updating
static::updating(function ($obj){
$obj->company_id = Session::get('company_id');
});
}
}
You can extend the BaseModel class on all the models that you want the company_id to be inserted or updated. Take a look at Eloquent Model Events for more information.
The above code will automatically insert or update the company_id to the model that you extend the BaseModel to. When you do a Model::all() or Model::get(), you automatically get the company_id on that Model and you can also perform searches as you requested on Point `
Hope this helps.
well, you can just add the company id to the find query.
Model::where("company_id","=",$company_id):
Or you can create a scope:
class theModel extends Eloquent {
static $company_id;
static for_company($company_id){
self::company_id=$company_id;
return __CLASS__;
}
public function scopeCompany($query)
{
return $query->where('company_id', '=', self::company_id);
}
}
//And later
$scope=theModel::for_company($company_id);
$res=$scope::company->where(...);
Disclaimer: I haven't tried this. Just a solution I constructed. Let me know if this works. This will not work under PHP 5.3

Help with LinqtoSql

Im using the Repository pattern and I want to write a method that receives a role and returns an Iqueryable of the users that belong to that role. (Im not sure if the right way would be to receive the role object or the role_id... in any case, how can I do this?? I dont like the query structure, I prefer the method structure of linq.
users and roles is many to many with a users_roles join table.
private ClasesDataContext db = new ClasesDataContext();
public IQueryable GetByRole(Role role)
{
return db.Users.Where();
}
Maybe try something like:
public IQueryable<User> GetByRoleId(Role role) {
return db.UsersRoleJoinTable.Where(ur => ur.Role == role).select(ur => ur.User);
}
Where UsersRoleJoinTable is your many-to-many join table.
Hope it helps.
Update: the select(ur => ur.User) is telling linq that for every row returned by "db.UsersRoleJoinTable.Where(ur => ur.Role == role)" we want to get the user associated with the UsersRoleJoinTable object. If you wanted a list of user ids instead, you could tell linq to select only user.id by doing select(ur => ur.id). Think of linq's select as a some sort of "for every row do this and put it in the list returned instead of the original row"
There is one downside to this approach tho, I believe in this case Linq is generating the sql to get the rows from the Join table (UsersRoleJoinTable) and then for every row returned, is executing another query to look up the User. I might be wrong on this, so to check the SQL generated by Linq do:
string sql_query = db.UsersRoleJoinTable.Where(ur => ur.Role == role).select(ur => u.User).ToString();
and then print the value of sql_query or watch it in debug mode. If Linq is in fact doing multiple queries, then I think the best solution is to create a view or stored procedure in SQL Server to get the users associated with the role and then add the view or stored procedure to Visual Studio designer so that you can call the view like:
db.GetUsers(role_id) //if using a GetUsers stored procedure
or
db.UsersByRoleView.where(ur => ur.role_id == passed_role_id) //if using a UsersByRoleView view
If you have an instance of the Role object
public IQueryable<User> GetByRole(Role role) {
return db.Users.Where(u => u.Role == role);
}
would work.
If you don't but just know the Id or some other property of the role something like this might be better.
public IQueryable<User> GetByRoleId(int roleId) {
return db.Users.Where(u => u.Role.Id == roleId);
}