How to create a trigger to populate a table in database based on saved data in another table from laravel controller - sql

I am creating an API with Laravel where I am trying to populate a table in a database based on saved data in another table in the database. So after table 1 data is saved, it should trigger for table 2 data to be also saved.
Here is the code for Controller of table 1
public function create(Request $request)
{
//
$this->validate($request, [
'name' => 'required|unique:users',
'code' => 'required'
]);
Auth::user();
$user = new User();
$user->inputed_code = $request->inputed_code;
$user->code = $request->code;
$user->save();
return response([
'success' => true,
'message' => 'Data has been saved!!',
'data' => $user,
], Response::HTTP_OK);
}
Here is the Controller for table 2 which I want to populate based on result from table 1 through a trigger effect.
public function reward(Request $request)
{
//
$reward = new Reward();
$reward->user_id = DB::table('users')->get(id)
$reward->points = $request->referral_reward;
$reward->save();
$user = $request->only('user_id');
$reward = Reward::where('user_id', '=', $user)->update([
'referral_reward' => 5
]);
return response()->json([
'success' => true,
'message' => 'You have been awarded 5 points',
'data' => $reward,
], Response::HTTP_CREATED);
}
Here are the API routes
Route::post('applycode', 'UserController#create');
Route::post('reward', 'RewardController#reward');
Please what am I doing wrong and is there is better way to approach this is give the same results.
Thank you

I understand that based on a saved User model you want to save something in your rewards table.
You could use Eloquent events. Register an event in your User model like this:
class User extends Model {
// ... other stuff
protected $dispatchesEvents = [
'saved' => UserSaved::class,
];
}
Then you need to create your events file by running php artisan make:event UserSaved, which creates a file app/Events/UserSaved.php. In this files __construct method you receive the affected model as parameter.
public function __construct(User $user)
{
// ... do sth
}
Then you need event listeners, see the docs, to consume the event
After defining and mapping your Eloquent events, you may use event listeners to handle the events.
Then inside the listener you can do all the logic you want to apply to your Rewards model.

Related

Laravel array of object update failing for unique email

I am using Laravel 6.I have an API that sends an array of staff, some are existing objects that need to be updated and some are new objects that need to be created. The creation part works but update fails due to "DUPLICATE ENTRY for UNIQUE EMAIL".I am using a function in controller
class AdminController extends Controller
{
public function userTransfer()
{
$caremanagementUserData = Admin::on('validated')->get();
$caremanagementUserData->each(function($userData)
{
if(User::where('email', '=', $userData['email'])->exists()) {
User::on('mysql')->where('id', '=',$userData['id'])->update([
'username' => $userData['username'],
'email' => $userData['email'],
'password' => $userData['password']
]);
} else {
User::on('mysql')->insert([
'username' => $userData['username'],
'email' => $userData['email'],
'password' => $userData['password']
]);
}
});
}
}
Help me about how to send email in the update array to solve the unique email error.
I found a way to get around the problem. Because email is unique and if email existence is being checked by
if(User::where('email', '=', $userData['email'])->exists())
{}
email update should be omitted from array of object, and if a new email is send it will be inserted as new data. It works for me !

Yii2 Active Record: The best way to display related records

I have two tables: record, user and a junction table record_user. I display the records, which are related to the logged user, using the Active Record.
In the model User I have the following functions:
public function getRecordUsers()
{
return $this->hasMany(RecordUser::className(), ['user_id' => 'id']);
}
public function getRecords()
{
return $this->hasMany(Record::className(), ['id' => 'record_id'])
->via('recordUsers');
}
See: http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#junction-table
In the model RecordSearch I define the query for the Active Data Provider:
public function search($params)
{
// The Object with all records, to which has the user access (the id of the user is in the junction table record_user).
$loggedUserRecords = YiiUser::findOne(Yii::$app->user->identity->id)->records;
// From the object is extracted an array with ids.
$loggedUserRecordsIds = yii\helpers\ArrayHelper::getColumn($loggedUserRecords, 'id');
// The ids are used to filter the Record object.
$query = Record::find()->filterWhere(['id' => $loggedUserRecordsIds]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
...
The code works as expected. But I would like to know, if there is a more direct way to display the related records, without the extraction of ids from the object.
I guess RecordSearch has this relations
public function getRecordUsers()
{
return $this->hasMany(RecordUser::className(), ['record_id' => 'id']);
}
public function getUsers()
{
return $this->hasMany(User::className(), ['id' => 'user_id'])
->via('recordUsers');
}
Or
public function getUsers()
{
return $this->hasMany(User::className(), ['id' => 'user_id'])->viaTable('recordUsers', ['record_id' => 'id']);
}
then you should be able to check query something like:
$dataProvider = new ActiveDataProvider([
'query' => Record::find()->joinWith(['users'])->where(['user_id'=> Yii::$app->user->identity->id]),
]);
if i didn't make a mistake.
As suggested by BHoft the fastest and most simple way is probably the following.
Model Record:
public function getRecordUsers()
{
return $this->hasMany(RecordUser::className(), ['record_id' => 'id']);
}
Model RecordSearch:
$dataProvider = new ActiveDataProvider([
'query' => Record::find()->joinWith(['recordUsers'])->where(['user_id'=> Yii::$app->user->identity->id]),
]);

How to retrieve associations together with authenticated user data?

I have a Users table and a UsersProfiles table - the two are obviously related and the user table stores basic user_id, username, password while the users_profiles table stores firstname, lastname, job_title etc.
In CakePHP 3, the call to Authentication Component on login returns the basic user table row. I would like to modify the same to also return the corresponding profile row. How can I do this?
I found a way to do it - but am not sure if there is a more elegant or simpler way.
public function login() {
if ($this->request->is('post')) {
$user = $this->Auth->identify();
if ($user) {
// load profile and associate with user object
$profile = $this->Users->UsersProfiles->get($user['id']);
$user['users_profile'] = $profile;
$this->Auth->setUser($user);
return $this->redirect($this->Auth->config('loginRedirect'));
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}
The contain option
Before CakePHP 3.1, use the contain option
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'contain' => ['UsersProfiles']
]
]
]);
A custom finder
As of 3.1 you can use the finder option to define the finder to use for building the query that fetches the user data
$this->loadComponent('Auth', [
'authenticate' => [
'Form' => [
'finder' => 'auth'
]
]
]);
In your table class
public function findAuth(\Cake\ORM\Query $query, array $options)
{
return $query->contain(['UsersProfiles']);
}
Both solutions
will ensure that the data returned by AuthComponent::identify() will contain the associated UsersProfiles.
See also
Cookbook > ... Components > Authentication > Configuring Authentication Handlers
Cookbook > ... Components > Authentication > Customizing find query

ZF2 / doctrine ORM authentication different Entity

in my application (ZF2 / ORM) i have 3 Entities (with Single Table inheritance)
User
Owner extends User
Agent extends User
i want to make one single Authentication (Login) for the 3 Entity using
doctrine.authenticationservice.orm_default
module.config.php
//other doctrine config
'authentication' => array(
'orm_default' => array(
'object_manager' => 'Doctrine\ORM\EntityManager',
'identity_class' => 'Application\Entity\User',
'identity_property' => 'email',
'credential_property' => 'password',
'credential_callable' => function(User $user, $passwordGiven) {
return $user->getPassword() == md5($passwordGiven);
},
),
),
and the process of login
//LoginController.php
// ..data validation
$this->authService = $this->getServiceLocator()->get('doctrine.authenticationservice.orm_default');
$AuthAdapter = $this->authService->getAdapter();
$AuthAdapter->setIdentity($this->request->getPost('email'));
$AuthAdapter->setCredential(md5($this->request->getPost('password')));
$result = $this->authService->authenticate();
if($result->isValid()){
$identity = $result->getIdentity();
//continue
}
how can i do this process without caring about object type,
when i try to login with email of an Agent, i get this error
Catchable fatal error: Argument 1 passed to Application\Module::{closure}() must be an instance of User, instance of Application\Entity\Owner given
The error you mention is due to the type hint on:
function(User $user) {
Which leads me to believe that you have a missing namespace declaration in your config file; in which case you can either add it or use the FQCN.
function(\Application\Entity\User $user) {
Nevertheless, I don't think it's actually the problem. You can only define one 'identity_class' with doctrine authentication (which the adapter will use to load the entity from the entity manager). If you have multiple entity classes there is no way to have each of these tested with one adapter.
However, the configuration is really just creating a new authentication adapter, specifically DoctrineModule\Authentication\Adapter\ObjectRepository. One solution would be to create multiple ObjectRepository adapters, each with the correct configuration for the different entities and then loop through each of them while calling authenticate() on the Zend\Authentication\AuthenticationService.
For example :
public function methodUsedToAutheticate($username, $password)
{
// Assume we have an array of configured adapters in an array
foreach($adapters as $adapter) {
$adapter->setIdentity($username);
$adapter->setCredential($password);
// Authenticate using the new adapter
$result = $authService->authenticate($adapter);
if ($result->isValid()) {
// auth success
break;
}
}
return $result; // auth failed
}
As previously mentioned is the doctrine config will not allow for more than one adapter, so you would need to create them manually and remove your current configuration.
Another example
public function getServiceConfig()
{
return [
'factories' => [
'MyServiceThatDoesTheAuthetication' => function($sm) {
$service = new MyServiceThatDoesTheAuthetication();
// Assume some kind of api to add multiple adapters
$service->addAuthAdapter($sm->get('AuthAdapterUser'));
$service->addAuthAdapter($sm->get('AuthAdapterOwner'));
$service->addAuthAdapter($sm->get('AuthAdapterAgent'));
return $service;
},
'AuthAdapterAgent' => function($sm) {
return new DoctrineModule\Authentication\Adapter\ObjectRepository(array(
'object_manager' => $sm->get('ObjectManager'),
'identity_class' => 'Application\Entity\Agent',
'identity_property' => 'email',
'credential_property' => 'password'
));
},
'AuthAdapterOwner' => function($sm) {
return new DoctrineModule\Authentication\Adapter\ObjectRepository(array(
'object_manager' => $sm->get('ObjectManager'),
'identity_class' => 'Application\Entity\Owner',
'identity_property' => 'email',
'credential_property' => 'password'
));
},
// etc...
],
];
}
Hopefully this gives you some ideas as to what is required.
Lastly, if you would consider other modules, ZfcUser already has a 'chainable adapter' which actually does the above (but uses the event manager) so It might be worth taking a look at even if you don't use it.

Yii Model with combined tables using relations in Active Record

I've got the model class Contactindiv with relations and search as follows.
public function relations()
{
return array(
'contactlogs' => array(self::HAS_MANY, 'Contactlog', 'ContactIndivID'),
);
}
public function search()
{
$criteria=new CDbCriteria;
$criteria->compare('ContactIndivID',$this->ContactIndivID);
$criteria->compare('PersonalTitle',$this->PersonalTitle,true);
$criteria->compare('NameLast',$this->NameLast,true);
$criteria->compare('NameMiddle',$this->NameMiddle,true);
$criteria->compare('NameFirst',$this->NameFirst,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
The current page shows the data in a searchable CGridView format.
My goal is to combine the 'contactlogs' from relations into the Model in order to have it show up on the page in a searchable fashion in the GridView. Basically add a searchable GridView column for each contact showing their contact log.
Thanks ahead of time for your help!
For your first goal (show contactlogs in model) you can write a getter in your main model. It depends, what you want to show in your gridview column but you could use something like:
public function getContacts()
{
$names = array();
foreach($this->contactlogs as $log)
$names[] = $log->name;
return implode(', ', $names);
}
Now you can use contacts as if it where a regular attribute of your "Contactindiv" model.
For your second goal you could add a public property which will contain the filter value, and which you can use in your search() method:
public $contactFilter;
public function search()
{
// ...
if(!empty($this->contactFilter)) {
$criteria->with = array(
'contactlogs' => array(
'joinType' => 'INNER JOIN',
'on' => 'contactlogs.name = :name',
),
);
$criteria->params[':name'] = $this->contactFilter;
}
// ..
}
Now you only need to add all the above in your gridview's columns configuration:
array(
'name' => 'contacts',
'filter' => CHtml::activeTextField($model, 'contactFilter'),
)
Please note, that i'm writing most of this from the top of my head and couldn't fully test it. But it should hopefully make the basic concept clear to you. Please let me know if it works.