I have a table that tracks a customer's status as stored in a third party database. My table should only be updated when I can successfully update the other database via an API call.
When using Doctrine, is it a bad practice to add the API call into the setter method in my entity class? For example:
public function setCustomerStatus( $cusotmerStatus )
{
$api = new externalApi();
if( $api->updateStatus( $customerStatus ) )
{
$this->customerStatus = $customerStatus;
}
else
{
return 'Could not update customer status';
}
}
If you have an Entity field that can only be set under a certain condition, you have two options; either make the check before the update:
if($api->updateStatus($customerStatus){
$entity->setCustomerStatus($customerStatus);
}
Or, make the check within the Entity, such as you have done in the set method. The advantage of containing it within the set method is that you don't leave room for error; unfamiliar developers may not know to run the check prior to calling the set method, or you just may forget. Therefore, if you can guarantee the check need always be made, I prefer the option you have chosen
Related
I'm making a simple API endpoint that returns an access code for an event.
If the event does not have access code, then it gets assigned one and saved in the database. Then, it checks if it's currently public or private. If private, return access code, if public, return empty string.
This is the endpoint controller:
public function getAc($eventId) {
// Pull event
$event = $this->eventService->api->getEventForce($eventId);
// If no access code for the event, generate one and update event record accordingly
if ($event->access_code == null) {
$access_code = $this->generateAccessCode();
DB::update('update events set access_code = ? where id = ?', [$access_code, $eventId]);
// Load updated event from DB.
$event = $this->eventService->api->getEventForce($eventId);
}
// Is the event currently private? return access code
if ($event->privacy=='private') {
return $event->access_code; // HERE: value comes back from the API but on MySQL Workbench it's still null.
}
// Is it public ? return empty string.
else {
return '';
}
}
My problem is that even though everything works as expected. When access_code is created it does come back from the api.
However when I check the record on MySQL Workbench (that connects to AWS Instance) it's still null! event though I pulled it from the database as a non-null value using the API endpoint.
Little confused with your code. From debugging I'd suggest checking your API for this issue. From what I can see you're doing this:
Ask API for event with ID 1
Check if event has a parameter
If no parameter, update using local DB
So I'm left asking, if the problem is with the API, why are you updating using the local instance of the DB? Furthermore could this be resolved using events? (I'm going to call your class something other than event so not to get confusing)
For instance:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Party extends Model {
public $table = 'parties';
public $fillable = [];
public static function boot() {
parent::boot();
static::creating(function($party) {
//create some fancy access code
$access_code = 'heyyyy';
//Check not manually set
if (!isset($party->attributes['access_code']) || is_null($party->attributes['access_code'])) {
$party->access_code = $access_code;
}
}
}
}
Now every time you create an event or 'party' using Party::create(); or $party = new Party; $party->save(); the creating event will pick up the save and also assign the access_code if you haven't set it manually like $party->access_code = 'you can\'t come';.
That's my thought anyway. However in your immediate case I think you need to ask yourself some more questions like:
Does the DB object from the instance of Laravel I'm using have access to the database to save said object?
Do I need to call the API in order to update my entity/model?
If the instance of Laravel I'm using from the \DB::update call have the same credentials as my API?
If this is a command or job, do my code changes affect it? Do I need to restart a supervisor/cron command to re-instance my code?
Who wrote this API? Is it reliable enough to use? And does it have documentation?
I am evaluating the CQRS pattern and wonder what would be the best way to obtain an Entity created by a command in the same action so I can render it in the view.
The two options I can think of are.
1) Create an id in the controller and send it with the command to fetch the entity by finding it by id.
2) Create an instance of the entity and send it with the command so I have a reference to it after it's populated
Example code
public function createEntityAction(array $data) {
$eventDispatcher = $this->get('event_dispatcher');
$eventDispatcher->dispatch(
CreateEntityHandler::name, // Handler
new Entity($data) // Command
);
// Placeholder //
$entity = get-the-created-entity
// //
return $this->view($entity, Response::HTTP_OK);
}
Second option is not really an option. "Entity creation", which is in fact is a business operation, is a command handling.
Generally speaking, the one who sends a command, whose handler creates an entity, should send the entity id with it. In what way the identity is generated is just an implementation concern.
Usually, command handlers either do what they suppose to do and return nothing (or ACK) or throw (or NAK).
This is a fairly basic question about CakePHP, but since my knowledge of this framework is rather rusty, it is making me lose a lot of time.
I have a ManyToMany relation between Guest and Present. Whenever a new Guest is created and associated with a present, I would like to mark the Present as taken. If the present is already taken, some error should arise. The reason why I am not just declaring that a Guest hasMany Presents is because in the future things may change and more than one guest could associate to a present, so I prefer to avoid a Db migration.
My Guest::add() action looks like follows. It is called with a POST with the data of a new Guest and the id of an existing Present.
public function add() {
if ($this->request->is('post')) {
$id = $this->request->data['Present']['id'];
$this->Guest->create();
$present = $this->Guest->Present->findById($id);
if ($present['Present']['taken']) {
throw new ForbiddenException();
}
if ($this->Guest->save($this->request->data)) {
if ($this->Guest->Present->saveField('taken', true)) {
// Give the guest a uuid and proceed with a welcome message
$this->Guest->read();
$this->set('uuid', $this->Guest->data['Guest']['uuid']);
}
}
}
else {
throw new ForbiddenException();
}
}
What happens is that a new Guest is created (correct) and associated with the given present (correct) but when I save the taken field a new present is created instead of modifying the given one.
What is the correct way to proceed to update the current Present?
If it is of any help, I am using CakePHP 2.0
For obtaining the model data by the primary key it's better to use theIn addition read method:
$present = $this->Guest->Present->read(null, $id);
The read method sets the model's id attribute so that further calls to other methods affect the same data record, rather than creating a new one. This should solve the problem you are having.
Model callbacks tend to be better suited for these situations. You could add a beforeSave callback to the Guest class to checks if the present is already taken, and not allow the creation if it is. This way the model logic is left in the model layer and you don't need to do any extra work e.g. if the constraint has to be enforced also when existing Guests are saved, or created from different controllers or actions.
It sounds like the ID of the model you are trying to save is losing scope. You should be able to resolve your issue by updating your code:
...
if ($this->Guest->save($this->request->data)) {
$this->Guest->Present->id = $id;
if ($this->Guest->Present->saveField('taken', true)) {
...
First, let me say, that I find the sfFormPropel form's interface inconsistent.
There is bind(), which returns nothing, but triggers validation, save() which returns the saved object, and bindAndSave(), which returns boolean, actually the return value of isValid(). Now, I have a working application, but I don't feel the code is right, and I'm quite new to symfony, so perhaps I'm missing something.
The object I need to create needs some external properties, that are not presented in the form, are external to the model, and are handled by the application (for example, the userId of the user, that created the entity, an external-generated guid, etc.).
Right now the flow is as follows:
get values from request and bind them to form
check if form is valid
if it's valid, add additional values and bind them to form one more time
save the form and return the object
The obvious answer would to add application-specific values to the values, retrieved from request, but It does not make sense to bind the application-specific values if the form is not valid, since they can be potentially expensive operations, may create database records, etc. Additionally, it should not be possible to pass those values with the post request, they should come from application only.
Now, I though that I have to let the model do these things, but since the data is external to the model, action still need to pass it to the model. The problem is, if I call $form->getObject() after bind(), it still has the old data, and not the data submitted.
What is the correct way to implement this kind of post-processing?
Second bounty is started to award the other valuable answer
The correct way would be setting your default values on the object you are passing to the form constructor. For example if you want to set the logged in user id on an object you are creating:
$article = new Article();
$article->setUserId($this->getUser()->getId());
$form = new ArticleForm($article);
if ($request->isMethod('post')) {
$form->bind($request->getParameter('article'));
if ($form->isValid()) {
$form->save();
}
}
Likewise for existing object, you can load the record and change any properties before passing it to the form constructor.
EDIT:
If you want to modify the object after validating, you can use $form->updateObject() like Grad suggests in his response. If the generated values depend on the submitted values, you can override sfFormObject::processValues():
class UserForm {
public function processValues($values) {
$values['hash'] = sha1($values['id'] . $values['username']);
return parent::processValues($values);
}
}
In case you need something from the action, you can always pass it as an option to the form:
$form = new UserForm($user, array('foo' => $bar));
That way, you can use $this->getOption('foo') anywhere in your form code, eg. in processValues().
It kind of depends of who has "knowledge" about the extra attributes. If they're really request specific, thus need to be processed in the controller, I go for binding, testing if valid and then update the bound object. To get the updated object with the bound (and validated) fields use the updateObject function.
$form->bind(..)
if ($form->isValid()) {
$obj = $form->updateObject(); // Updates the values of the object with the cleaned up values. (returns object)
$obj->foo = 'bar';
$obj->save();
}
But since this normally is also behaviour that is form specific, I usually go for overriding the Form class. By overriding the doUpdateValues() function you can easily access submitted data, and append your own data. Of course you can also go higher in the chain, and override the save() function.
To set custom data for this form, you can also 'publish' public methods, which can then be used by the controller.
My brain is starting to hurt so I decided I'd ask here.
I have a data object, Employee. Getters, setters, formatters, etc. I have a manager, EmployeeManager, which handles database access and other things. Right now I have a large validation block in EmployeeManager, but I've been wondering if I could move some of that to the setters.
For example, right now I have;
public function getSSN($bFormatted = true) {
return ($bFormatted) ? $this->format_ssn($this->SSN) : $this->SSN;
}
public function setSSN($s, $bValidate = false)
{
// If we're validating user entry, save a copy.
// Either way, store a trimmed version.
if ($bValidate): $this->SSNToValidate = $s; endif;
$this->SSN = str_replace('-', '', $s);
}
public function getSSNToValidate() { return $this->SSNToValidate; }
What this does is:
* When you set an SSN, if it's being done by the system (like from the database) then it does setSSN('123456789', false), because SSNs are stored in the database sans dashes.
* When you set an SSN from user input, it does simply setSSN('123-45-6789') then not only trims the dashes, but also stores a raw version to validate (because I want to validate based on format)
* When you get an SSN, if formatting is requested (and it always is except when you're writing to the database), it formats it based on another function in the Employee class.
So my question is: Could I perhaps add the validation to the setter here, instead of relying on the monolithic validate function in the Manager class? Because I'm starting to have to deal with errors coming from all over the application, I've decided for the moment to move to a central Error Handler static class, rather than each manager maintain its own list of errors.
And because of this, I could easily add error handling to this:
public function setSSN($s, $bFromUser = false)
{
if ($bFromUser && !$this->validateSSN($s))
{
ErrorHandler::add(array('ssn' => 'Invalid SSN entered'));
}
else
{
$this->SSN = str_replace('-', '', $s);
}
}
So I guess my question is: Does this make sense at all or am I hosing myself by moving validation from the manager (to be performed on demand or just before writing to the database) to the object (to be performed on entry)?
This is overall generic, I'm just using SSN as a good example.
You should always validate when you're creating an object or setting values in the object. This way you are always guaranteed to have an object that is in a valid state. If the validation is not in the object itself you can not guarantee that the object will ever be validated.
Example validation outside of the object:
In this case there is a bug and we forgot to validate SSN.
class EmployeeManager
{
function saveEmployee($employee)
{
//Oops, we forgot to validate SSN
$db->save($employee); //This is just SQL to persist the employee.
}
}
//Somewhere else in code...
$employee = new Employee();
$employee->setSSN("FredFlintstone"); //No validation is done here.
$employeeManager = new EmployeeManager();
$employeeManager->saveEmployee($employee); //This call will persist FredFlintstone because validation was missed.
Example validation inside of the object:
In this case the employee object validates all of it's input. This way we know that if we have a instance of an employee all the data within it is valid.
class Employee
{
function setSSN($input)
{
if(strlen($input) != 9)
{
throw new Exception('Invalid SSN.');
}
//Other validations...
$this->ssn = $input;
}
}
//Somewhere else in code...
$employee = new Employee();
$employee->setSSN("FredFlintstone"); //This call will now throw an exception and prevent us from having a bad object.
$employeeManager = new EmployeeManager();
$employeeManager->saveEmployee($employee);
In addition you should never allow an object to be created that is not fully initialized. If say a SSN is required for an employee then don't supply an empty constructor. Your constructors should have parameters for all required fields (unlike my example which I omitted them for clarity).
The general rule of thumb with service-based applications is that validation should ALWAYS occur at the server end, shortly before persistence. Optionally you can perform validation on the client for a better user experience. But it's not safe to only perform validation at the client (ie, in your setter). Never rely on client data being right.
When doing client-based validation, it's preferable for each class (whether it's the model, view model, presenter, etc, depending on your architectural pattern) to validate itself rather than rely on some outside validator.