How to view own entities in yii2? - yii2

I have User table which has a field called 'society_id' Which defines which society the user belongs to. Similarly, I have 'society_id' field in another table called 'expense_details' which identifies the society_id of the user who has entered the data in 'expense_details'.
this is my user table
https://i.stack.imgur.com/1hBky.png
this is my expense-details table
https://i.stack.imgur.com/Z3cQU.png
I know we can access the society_id of logged in user like this :
I want Logged in users to access their view but I want the user not to access data from table 'expense_details' related to other users with change url.
I know we can get society_id of logged in user like this Yii::$app->user->identity->society_id
But I am wondering how can i use it here and what changes i am supposed to make in my actionView and/or Model.
Here is my Expensedetails view controller.
public function actionView($id) {
$details = \app\models\ExpenseDetails::find()->where(['expense_id' => $id])->all();
$searchModel = new \app\models\ExpenseDetailsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$dataProvider->query->where("expense_id=$id");
return $this->render('view', [
'model' => $this->findModel($id),
'details' => $details,
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
PS : English is not my native language. I am newbie to yii2 and stackoverflow, please excuse me for the mistakes. Thanks.

I solved it.
In My ExpenseDetails Model
protected function findModel($id)
{
if (($model =ExpenseDetails::findOne($id)) !== null) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}
In My Expensedetails view controller
protected function findModel($id)
{
if (($model = ExpenseDetails::findOne($id)) !== null) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}

As each user has society_id which can be same for any 2 user's records, so when you will fetch the table like this:
ExpenseDetails::find()->where(['expense_id' => $id, 'society_id' => Yii::$app->user->identity->society_id])->all();
it will return all the records on specific expense_id and specific society_id but those records would be of multiple users, if you want another condition that one user can not access anothers user's record, you can add "user_id" attribute in "expense_details" table and set the "users" table "id" attribute to it, as per need so you can fetch the records with specific expense_id, society_id and specific "user_id":
ExpenseDetails::find()->where(['expense_id' => $id, 'society_id' => Yii::$app->user->identity->society_id, "user_id" => 1])->all();
or for current logged in user :
ExpenseDetails::find()->where(['expense_id' => $id, 'society_id' => Yii::$app->user->identity->society_id, "user_id" => Yii::$app->user->id])->all();

Related

Custom Data Provider using foreach yii2

I'm trying to create a custom dataProvider made out of Professor's id’s (teachers id’s). My site hosts information about teachers, courses, grades, etc. for schools.
ER model
The user Alumno (student) will see a gridview of it’s professors for each course he’s registered in, so, I’m trying to return that information in a dataProvider.
First, I ask if the user is an Alumno.
Then I search for the Asignaturas the Alumno is registered in.
With that information, I search for the professors that teaches that Cursos, to return it’s id’s as a dataProvider, so I made a foreach cycle.
What I need is an array of Profesores id’s so I can show Profesors’s names in the grid view. The actual code is querying the last Profesor id into the dataProvider.
public function actionIndex()
{
$this->layout = 'main';
$searchModel = new ProfesorSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
if(User::isUserAlumno(Yii::$app->user->identity->id)){
$alumno = Alumno::find()->where(['id_usuario' => Yii::$app->user->identity->id])->one();
$asignaturas = Asignatura::find()->where(['id_curso' => $alumno->id_curso])->all();
foreach ($asignaturas as $asignatura){
$dataProvider = new ActiveDataProvider([
'query' => Profesor::find()->where(['id' => $asignatura->id_profesor])
]);
}
}
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
Any help would be appreciated.
You can use column() to get all id_profesor from query.
$profesorIds = Asignatura::find()->select('id_profesor')->where(['id_curso' => $alumno->id_curso])->column();
$dataProvider = new ActiveDataProvider([
'query' => Profesor::find()->where(['id' => $profesorIds])
]);

How can I display in view multiple GridViews with one DataProvider group it by some field?

A have a table 'operations' with finance operations for all users, including 'id', 'user_id', 'sum', 'name', 'date_picked' etc.
I have created one ActiveDataProvider that gets all operations for concrete User. Now I would like to display in view data from this dataprovider - not in one GridView, but in several GridViews which data grouped by 'date_picked' value.
I know that I can make a new dataprovider for the each 'date_picked' but there will be a large amount of requests to database.
Is there a way to display grouped data in several GridViews based on one dataprovider's data?
Controller:
/** #var OperationComponent $comp */
$comp = Yii::$app->operation;
$userId = Yii::$app->user->id;
$filterModel = $comp->getOperationSearch();
$operations = $comp->getSearchProvider($userId, Yii::$app->request->get());
OperationComponent:
public function getOperationSearch()
{
return new OperationSearch();
}
public function getSearchProvider($user_id, $params)
{
$model = new OperationSearch();
return $model->search($user_id, $params);
}
OperationSearch:
public function search($user_id, $params)
{
$query = Operation::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
// сортировка по умолчанию
'sort' => [
'defaultOrder' => [
'date_picked' => SORT_DESC
]
]
]);
return $dataProvider;
View:
....
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $filterModel,
.....
]); ?>
....
There is an Yii2 extension for complex GridViews: kartik-v/yii2-grid
It contains collapsible rows for showing details for a row.
You can view a demo here.
What you are looking for is the Expand Row Column Widget. It allows to expand a row by just loading the required data for this row details.
This extension has a good documentation is quite stable and tested and can be used straight forward.

Login access not working in yii2

Im using yii2 for my project. I need to use two different tables for login (Login page is same). I have two models Admin and User. And i have one LoginFrom for login.
I can login properly but the problem is after logged in i cannot get whether the admin is logged in or the user is logged in.
I have set it in config file (web.php) like below:
'admin' => [
'identityClass' => 'app\models\Admin',
'enableAutoLogin' => false,
'class' => 'yii\web\User',
'authTimeout' => 1200, // in Seconds. 1200 seconds means 20 mins
],
'user' => [
'identityClass' => 'app\models\User',
'enableAutoLogin' => false,
'authTimeout' => 1200
],
So im getting logged in user details by using below method:
\Yii::$app->admin->identity;
\Yii::$app->user->identity;
My problem is if im logged in as admin i can get user values also by using this : \Yii::$app->user->identity; or if im logged in as user i can get admin values by using this : \Yii::$app->admin->identity;.
My LoginForm.php is :
<?php
namespace app\models;
use Yii;
use yii\base\Model;
class LoginForm extends Model
{
public $username;
public $password;
public $rememberMe = true;
private $_user = false;
public function rules()
{
return [
// username and password are both required
[['username', 'password'], 'required'],
// rememberMe must be a boolean value
['rememberMe', 'boolean'],
// password is validated by validatePassword()
['password', 'validatePassword'],
];
}
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password)) {
$this->addError($attribute, 'Incorrect username or password.');
}
}
}
public function login()
{
if ($this->validate()) {
if(!empty($this->getUser()['phone_number'])) {
return Yii::$app->admin->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
} else {
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
}
}
return false;
}
public function getUser()
{
if ($this->_user === false) {
$this->_user = User::findByUsername($this->username);
if(!$this->_user) {
$this->_user = Admin::findByUsername($this->username);
}
}
return $this->_user;
}
}
I cant find the problem and if i logged in identity creating for both the users so i could'nt write access rules in particular controller to allow admin only to access the controller.Please help me :(
From reading the comments I think you should just create a unifying table for the two identities where they both get their IDs from. Then make that the identity class. The reason you are able to see the details in both identity classes is that they have the same ID.

HybridAuth CakePHP3.X,how to save user after successful login?

I have read the description over,
Once a user is authenticated through the provider the authenticator gets the user profile from the identity provider and using that tries to find the corresponding user record in your app's users table. If no user is found and registrationCallback option is specified the specified method from the User model is called. You can use the callback to save user record to database.
But where to define/declare registrationCallback
If you want user to register if not exist then this code will execute :
if (!empty($this->_config['registrationCallback'])) {
$return = call_user_func_array(
[
TableRegistry::get($userModel),
$this->_config['registrationCallback']
],
[$provider, $providerProfile]
);
if ($return) {
$user = $this->_fetchUserFromDb($conditions);
if ($user) {
return $user;
}
}
You need to define the registration function in config ( in __construct) and regarding call_user_func_array read this link - https://php.net/call-user-func-array
To Store user in database after login
1>defines the function in UsersTabel.php
public function registration($provider, $profile) {
$user = $this->newEntity([
'username' => $profile->displayName,
'provider' => $provider,
'provider_uid' => $profile->identifier
]);
if(!$this->save($user))
{
Log::write(LOG_ERR, 'Failed to create new user record');
return false;
}
return true;
}
2>Replace the function of file vendor ▸ admad ▸ cakephp-hybridauth ▸ src ▸ Auth▸ HybridAuthAuthenticate.php
public function __construct(ComponentRegistry $registry, $config)
{
$this->config([
'fields' => [
'provider' => 'provider',
'provider_uid' => 'provider_uid',
'openid_identifier' => 'openid_identifier'
],
'hauth_return_to' => null,
'registrationCallback'=>'registration'
]);
parent::__construct($registry, $config);
}

Prevent show data from another user

How to prevent all detailView show data from another user ??
For example, this happens when you type an product ID of another user in the URL. The detailView shows the details of the product normally, however belongs to another User, and may even change it and delete it.
You can do something like this in the controller if you don't want to use RBAC :
protected function findModel($id)
{
//Check if the author is the current user
if (($model = Product::findOne($id)) !== null && $model->author_id==Yii::$app->user->id) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
Like this users which are not the author can't view, update or delete the product.
http://www.yiiframework.com/forum/index.php/topic/61915-prevent-show-data-from-another-user/page__view__findpost__p__274644
Several options:
1) simplest one, in the controller before showing the view check that the current user can see the product. If he cannot redirect him (by throwing an error) to a 404 page (or whatever error you want to show).
2) use RBAC to set up roles and what those roles can do. This is the most professional option
3) you may be able to modify the accessfilter to do this too too
If you need to ask how to do this go with option 1.
If you want option 2 or 3 start by reading this http://www.yiiframework.com/doc-2.0/guide-security-authorization.html
An example to what Mihai have suggested.
public function behaviors()
{
return [
'accessControl' => [
'class' => \yii\filters\AccessControl::className(),
'rules' => [
[
'actions' => ['view'],
'allow' => true,
'matchCallback' => function () {
$request = \Yii::$app->request;
$user = \Yii::$app->user->identity;
$product = Product::findOne($request->get('id'));
if ($user && $product->owner_id == $user->id) {
return true;
}
return false;
}
],
[
'allow' => false,
'roles' => ['*'],
],
],
]
];
}