Optimization - Two similar method - php

I'm new in Symfony and i have a little problem.
I been searching online, couldn't find the answer to my problem.
I create API and I have two controllers(UserController, InstructorController) with similar method (addUserImage, addInstructorImage). I created abstract BaseImage(Here is saving files), InstructorImage and UserImage(here are set path). This is my controller:
/**
* #ApiDoc(
* name="addInstructorImage", section="Instructors",
* description="Add Instructor Image",
* )
*
* #Route("/instructor/{instructor}/image", name="instructors.image.add")
* #Method("POST")
*
* #param Request $request
* #param Instructor $instructor
* #View
*
* #return \FOS\RestBundle\View\View
*/
public function addInstructorImage(Request $request, Instructor $instructor)
{
$this->denyAccessUnlessGranted('edit', $instructor->getUser());
$image = new InstructorImage();
$form = $this->get('form.factory')
->createNamed('', InstructorImageType::class, $image, ['csrf_protection' => false, 'method' => 'POST']);
$form->handleRequest($request);
if ($form->isValid()) {
$image->setInstructor($instructor);
$em = $this->getDoctrine()->getManager();
$em->persist($image);
$em->flush();
return $this->view(null, Response::HTTP_NO_CONTENT);
}
return $this->view($form, Response::HTTP_BAD_REQUEST);
}
My second controller is identical.
The only difference is another object , and another form. What is the best way to optimize this code. Should I create services that adds photos or use chain handler? Maybe You have better ideas?
Thank you for your help

Short answer: Don't waste time on trying to optimize this sort of stuff.
Your method consist of 19 lines of code. It's all basic boiler plate easy to read code. I know whenever I see duplicate code I'm tempted to try and combine it somehow but what exactly would be gaining? Reducing 19 lines to maybe 15?
If I was going to change your code then I'd be tempted to move some of the business logic into it's own service. Something like:
InstructorManager::addImage($instructor,$image);
That would get rid of the entity manager boiler plate and provide a bit of abstraction. Might make it a bit easier to test though all the method is doing is setting the image and calling flush. Hardly worth the effort. Might be worth it if you have other manager type functionality to add as well. Or maybe you want to be able to add images from a console app.
Of course you may end needing to add functionality in the future. Maybe you want to notify someone when the image is changed. If you find yourself needing to modify duplicated code then you can probably justify the effort of moving common code into it's own service.
And I suppose you could create a service for your form using the container's factory capability. Especially if you had a bunch of these form to make. But again, hardly worth the effort and might even make things more difficult to maintain.

Related

Laravel RESTful api - Dynamic number of query params

I have a simple website running Laravel Jetstream with Teams enabled. On this website, you can create different "to-do tasks", which are owned by a team. I have a model called Task.
I am trying to create a public facing API, so my users can query their tasks from their own applications. In my routes/api.php file, I have added this:
Route::middleware('auth:sanctum')->group(function(){
Route::apiResources([
'tasks' => \App\Http\Controllers\API\TaskController::class,
]);
});
And then in the TaskController, I have only begun coding the index method:
/**
* Display a listing of the resource.
* #queryParam team int The team to pull tasks for.
* #return \Illuminate\Http\Response
*/
public function index()
{
if(request()->team){
$tasks = Task::where('team_id', request()->team)->get();
return TaskResource::collection($tasks);
}
return response([
'status' => 'error',
'description' => "Missing required parameter `team`."
], 422);
}
Now this works fine. I can make a GET request to https://example.org/api/tasks?team=1 and successfully get all the tasks related to team.id = 1.
However, what if I want to include multiple query parameters - some required, others only optional. For example, if I want to let users access all tasks with a given status:
https://example.org/api/tasks?team=1&status=0
What is the best practices around this? As I can see where things are going now, I will end up with a lot of if/else statement just to check for valid parameters and given a correct error response code if something is missing.
Edit
I have changed my URL to be: https://example.org/api/teams/{team}/tasks - so now the team must be added to the URL. However, I am not sure how to add filters with Spatie's Query Builder:
public function index(Team $team)
{
$tasks = QueryBuilder::for($team)
->with('tasks')
->allowedFilters('tasks.status')
->toSql();
dd($tasks);
}
So the above simply just prints:
"select * from `teams`"
How can I select the relationship tasks from team, with filters?
The right way
The advanced solution, i have built a handful of custom ways of handling search query parameters. Which is what you basically wants to do, the best solution by far is the spatie package Query Builder.
QueryBuilder::for(Task::class)
->allowedFilters(['status', 'team_id'])
->get();
This operation will do the same as you want to do, and you can filter it like so.
?fields[status]=1
In my experience making team_id searchable by team and similar cases is not worth it, just have it one to one between columns and input. The package has rich opportunities for special cases and customization.
The simple way
Something as straight forward like your problem, does not need a package off course. It is just convenient and avoids you writing some boiler plate code.
This is a fairly simple problem where you have a query parameter and a column you need to search in. This can be represented with an array where the $key being the query parameter and $value being the column.
$searchable = [
'team' => 'team_id',
'status' => 'status',
];
Instead of doing a bunch of if statements you can simplify it. Checking if the request has your $searchables and if act upon it.
$request = resolve(Request::class);
$query = Task::query();
foreach ($this->seachables as $key => $value) {
if ($query->query->has($key)) {
$query->where($value, $query->query->get($key))
}
}
$tasks = $query->get();
This is a fairly basic example and here comes the problem not going with a package. You have to consider how to handle handle like queries, relationship queries etc.
In my experiences extending $value to an array or including closures to change the way the logic on the query builder works can be an option. This is thou the short coming of the simple solution.
Wrap up
Here you have two solutions where actually both are correct, find what suits your needs and see what you can use. This is a fairly hard problem to handle pragmatic, as the simply way often gets degraded as more an more explicit search functionality has to be implemented. On the other side using a package can be overkill, provide weird dependencies and also force you into a certain design approach. Pick your poison and hope at least this provides some guidance that can lead you in the right direction.

DDD - how to deal with get-or-create logic in Application Layer?

I have an DailyReport Entity in my Domain Layer. There are some fields in this object:
reportId
userId
date
tasks - Collection of things that user did in given day;
mood - how does the user felt during the whole day;
Also, there are some methods in my Application Service:
DailyReportService::addTaskToDailyReport
DailyReportService::setUserMoodInDailyReport
The thing is that both of these methods require DailyReport to be created earlier or created during function execution. How to deal with this situation?
I have found 2 solutions:
1 Create new DailyReport object before method dispatching, and after that pass reportId to them:
//PHP, simplified
public function __invoke() {
$taskData = getTaskData();
/** #var $dailyReport DailyReport|null **/
$dailyReport = $dailyReportRepository->getOneByDateAndUser('1234-12-12', $user);
//there were no report created today, create new one
if($dailyReport === null) {
$dailyReport = new DailyReport('1234-12-12', $user);
$dailyReportRepository->store($dailyReport);
}
$result = $dailyReportService->addTaskToDailyReport($taskData, $dailyReport->reportId);
//[...]
}
This one requires to put a more business logic to my Controller which i want to avoid.
2: Verify in method that DailyReport exists, and create new one if needed:
//my controller method
public function __invoke() {
$taskData = getTaskData();
$result = $dailyReportService->addTaskToDailyReport($taskData, '1234-12-12', $user);
//[...]
}
//in my service:
public function addTaskToDailyReport($taskData, $date, $user) {
//Ensure that daily report for given day and user exists:
/** #var $dailyReport DailyReport|null **/
$dailyReport = $dailyReportRepository->getOneByDateAndUser();
//there were no report created today, create new one
if($dailyReport === null) {
$dailyReport = new DailyReport($date, $user);
$dailyReportRepository->store($dailyReport);
}
//perform rest of domain logic here
}
This one reduces complexity of my UI layer and does not expose business logic above the Application Layer.
Maybe these example is more CRUD-ish than DDD, but i wanted to expose one of my use-case in simpler way.
Which solution should be used when in these case? Is there any better way to handle get-or-create logic in DDD?
EDIT 2020-03-05 16:21:
a 3 example, this is what i am talking about in my first comment to Savvas Answer:
//a method that listens to new requests
public function onKernelRequest() {
//assume that user is logged in
$dailyReportService->ensureThereIsAUserReportForGivenDay(
$userObject,
$currentDateObject
);
}
// in my dailyReportService:
public function ensureThereIsAUserReportForGivenDay($user, $date) {
$report = getReportFromDB();
if($report === null) {
$report = createNewReport();
storeNewReport();
}
return $report;
}
//in my controllers
public function __invoke() {
$taskData = getTaskData();
//addTaskToDailyReport() only adds the data to summary, does not creates a new one
$result = $dailyReportService->addTaskToDailyReport($taskData, '1234-12-12', $user);
//[...]
}
This will be executed only when user will log in for the first time/user were logged in yesterday but this is his first request during the new day.
There will be less complexity in my business logic, i do not need to constantly checking in services/controllers if there is a report created because this has been executed
previously in the day.
I'm not sure if this is the answer you want to hear, but basically I think you're dealing with accidental complexity, and you're trying to solve the wrong problem.
Before continuing I'd strongly suggest you consider the following questions:
What happens if someone submits the same report twice
What happens if someone submits a report two different times, but in the second one, it's slightly different?
What is the impact of actually storing the same report from the same person twice?
The answers to the above questions should guide your decision.
IMPORTANT: Also, please note that both of your methods above have a small window where two concurrent requests to store the rerport would succeed.
From personal experience I would suggest:
If having duplicates isn't that big a problem (for example you may have a script that you run manually or automatically every so often that clears duplicates), then follow your option 1. It's not that bad, and for human scale errors should work OK.
If duplicates are somewhat of a problem, have a process that runs asynchronously after reports are submited, and tries to find duplicates. Then deal with them according to how your domain experts want (for example maybe duplicates are deleted, if one is newer either the old is deleted or flagged for human decision)
If this is part of an invariant-level constraint in the business (although I highly doubt it given that we're speaking about reports), and at no point in time should there ever be two reports, then there should be an aggregate in place to enforce this. Maybe this is UserMonthlyReport or whatever, and you can enforce this during runtime. Of course this is more complicated and potentially a lot more work, but if there is a business case for an invariant, then this is what you should do. (again, I doubt it's needed for reports, but I write it here in the care reports were used as an example, or for future readers).

Integrating Mobile Money to Symfony

I have a Symfony application which I will like to integrate mobile money into. The problem is I cannot add PHP code to twig files and I am a complete newbie to this kind of challenge. The code reads:
<?php
require_once '/path/to/monetbil-php/monetbil.php';
// Setup Monetbil arguments
Monetbil::setAmount(500);
Monetbil::setCurrency('XAF');
Monetbil::setLocale('en'); // Display language fr or en
Monetbil::setPhone('');
Monetbil::setCountry('');
Monetbil::setItem_ref('2536');
Monetbil::setPayment_ref('d4be3535f9cb5a7aff1f84fa94e6f040');
Monetbil::setUser(12);
Monetbil::setFirst_name('KAMDEM');
Monetbil::setLast_name('Jean');
Monetbil::setEmail('jean.kamdem#email.com');
// Start a payment
// You will be redirected to the payment page
Monetbil::startPayment();
I am looking at adding this to App/Resources/Views/members/dashboard.html.twig
Twig is only ment to render your output. Put your (php) logic in your controller and/or create your own service. From your controller you will also render your Twig template with the variables you need but only to render the output that you want to show to the users.
If you're using a framework like Symfony, you shouldn't use require_once (except some exceptions). Read about autoloading and dependency injections (Symfony.com has excellent articles).
For some reason, the monetbil-php library doesn't use composer. I don't know why, but I can imagine three reasons: they don't know what it is (hello developers, it's 2017!), they hate other developers or the library hasn't been updated for years. Despite the recent commits, it looks like a very outdated library (why still supporting PHP 5.2? That's from the dark ages!). Sorry for this slightly offtopic rant, back to the question.
For now, copy the files to your project, give the file a namespace and use it in your project. I've opened an issue here because I think the developers should add a composer file if they want their users to use their library in a framework like Symfony.
The PHP code should be in your controller* since you can't use PHP in Twig and even if you could, you shouldn't. Monetbil defines business logic, so it shouldn't be in a template.
To use it in your controller:
/**
* Pay the bill
*
* #Route("/pay/{id}", name="payment")
* #Method("POST")
* #param Request $request
* #param Order $product
*
* #return JsonResponse
* #throws \Exception
*/
public function payAction(Request $request, Order $product)
{
Monetbil::setAmount(500);
//..
Monetbil::startPayment();
}
According to the comment, startPayment() will redirect, so there's nothing to return to the Twig template.
(*) Business logic in your template is considered as a bad practice, but you should prevent putting too many business logic in a controller too. If you have this example working, try to read about services so you can define the business logic in a framework-agnostic way. It makes maintaining your application (unit tests, Symfony updates, maybe switch to another framework?) easier.

Is there a need to abstract services by creating a "container" [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
I am currently working on a personal project and i have implemented service layers. I prefer to store services independently so i don't have a large library within one file. Brief Example of File Structure below
services/
user/
authentication
login
logout
registration
news/
articles
article
I know a few people who implement a userService class that will group everything that i currently have. I prefer my method to save time in future edits + i have a lot of user services/functionality so its better to keep it separate. I was recently advised to implement a userService class in the root of my services folder and use it to call/execute the services needed within the application. Below is my example/understanding
<?php
/**
*-----------------------------------------------------------------
*
* USER SERVICE CLASS
*
* Simplifies Service Usage Within Application
*
*/
namespace Service;
use \Helper\ServiceAccess;
class UserService extends ServiceAccess {
// Define Service Prefix Key
protected $prefix = 'user/';
}
///////////////////////////////////////////////////////////////////
// Separate File ( Helper Function )
///////////////////////////////////////////////////////////////////
/**
*-----------------------------------------------------------------
*
* SERVICE ACCESS LAYER
*
* Used to Simplify the Process of Executing Services, and Grouping
* Alerts For Simple Front-end Error Messages
*
*/
namespace Helper;
class ServiceAccess extends Base {
public $dependencies = ['factory'];
// Default Service Prefix
protected $prefix = '';
// Alert Container Used By Controller to Set UI Alerts
protected $alerts = [
'errors' => [],
'info' => [],
'success' => [],
'warning' => []
];
/**
* Service Execution Method
*
* Used Within Parent Service Classes Such as UserServices
* TournamentServices
* etc.
*
* #param string $key Refers to the Factory Key ( Excluding Prefix )
* #param mixed $input Any Type of Input to Be Passed to Execute Method of Child Service
*/
public function execute($key, $input = []) {
// Create Service Class Via Factory - Call Execute Method Within Service
$service = $this->factory->make($this->prefix . $key);
$execute = $service->execute($input);
// Get & Merge Alerts From Service
$this->setAlerts($service);
// Return Result From Service Execution
return $execute;
}
/**
* Set Alerts
*
* #param array $alerts Front-End User Alerts Defined By Services
*/
private function setAlerts($service) {
$this->alerts = [
'errors' => array_merge($this->alerts['errors'], (array) $service->get('errors')),
'info' => array_merge($this->alerts['info'], (array) $service->get('info')),
'success' => array_merge($this->alerts['success'], (array) $service->get('success')),
'warning' => array_merge($this->alerts['warning'], (array) $service->get('warning'))
];
}
}
CONTROLLER EXAMPLE
<?php
/**
*-----------------------------------------------------------------
*
* LOGIN CONTROLLER
*
*/
namespace Controller\www;
use \Helper\Controller;
class Login extends Controller {
public $dependencies = ['arena', 'login', 'notification', 'site', 'userservice'];
/**
* Login
*
* Login Runs Through Various Checks Including If User is Banned, Account is Locked,
* or Forgot Password Request Is Active. Then the Entered Password is Matched & if Valid
* User is Logged In
*/
public function index() {
// User Already Logged In Redirect
$this->user->get('id') ? $this->redirect->home() : '';
/**
* User Login
*
* If Successful, Login User, Redirect Home
* Else Set Error Alerts
*/
if ($this->form->post('login')) {
// Define and Sanitize Post Data
$input = $this->input->get(['username', 'password']);
// Execute Login Service Layer - Define Flash Alerts
$login = $this->userservice->execute('login', $input);
$this->alerts($this->userservice->get('alerts'));
// Redirect Home on Successful Login
$login === true ? $this->redirect->home() : '';
}
}
}
The execute method within the ServiceAccess class is what i was advised to do the rest i added for my user error handling. My questions are as follows
Why is this better than calling the services directly within the application?
It simplified the execution of the services/setting of alerts within my controllers ( turned ~15 lines of code into 4 lines within controller ) BUT i have services like user/transactions ( Handles credit/debit of user account ) and they have separate methods that need to be used. So i am wondering if it is the UserService Class or my transaction class that needs to be updated. I was thinking of defining an execution method within transaction and just passing a key within the input to define the type of transaction being used.
Is this the best route to go about accessing/implementing services within my application?
The purpose of abstraction is to manage underlying complexity. If the overall complexity of the system is reduced because of an additional layer, do it. If not, reconsider. Every layer in the architecture should have a purpose for being there. And this purpose must be communicated (documented) to the developers, otherwise everyone would just be stabbing in the dark.
A good way to plan your architecture is to draw architectural diagrams and assign roles to each layer. Each layer may be a single or a collection of classes. If you struggle to find a good, clean role for a particular layer, you may not need that layer after all.
I know you're using an MVC pattern in your architecture, but that does not constitute the entire architecture, just the broad approach. Think about the consumption and execution of your services and the various layers. Think about the layers you want in your architecture. Would a UserService class make sense in your architecture? What layer does it belong to? What is the role of that layer? Sometimes you want an additional abstraction layer to reduce duplicate code - like you seem to have achieved in your case; sometimes you simply want to do it the simple, intuitive way rather than over-complicate things.
Every architectural layer, every pattern, every abstraction can be well applied or badly abused. Does adding such a layer confuse you or other developers in the team? If it does, take it out. The application of every layer (abstraction) should be elegant and intuitive - it should make everyone else in the team go "Oh yea, this is smart." But if you end up in a situation where developers are unsure how to extend/maintain the code, it becomes counter-productive.
Without a good understanding of the intent of the entire application and the background of the developers (you), it is impossible to make a good choice or give good advice. So while I understand you're asking for a best practice - in essence, "how much abstraction should we apply in our architecture/framework?", there are really no correct answers. It depends on too many things - developer preference and even coding style included. Some would even say that MVC is the wrong thing to do on the server-side.
To conclude: I can't give you a direct "Yes" or "No" answer. And I don't think anyone can. The only advice anyone can give for an architectural framework: stay DRY but avoid over-engineering.

Save entities to a REST API instead of DB using Doctrine 2

This is related to my other question: Persisting entities using a REST API.
For a project in Symfony2 I need to be able to persist entities using an remote (third-party) RESTful API. I also want to be able to retrieve entities with data from that API.
In other words, my objects are saved in the third-party database. They are not saved in my own database. Whenever I need to save data, or find data, I use their REST API.
I have been pointed to several libraries, including one made by Doctrine itself. However, none of them offers me what I'm looking for. The one made by Doctrine is the best option, but uses the Active Record pattern and doesn't offer all the sweet Doctrine 2 stuff. Don't get me wrong, I've been using Active Record implementations for a long time, but I've fallen in love with Doctrine's Data Mapper pattern now.
Ideally, I'd like to be able to use Doctrine's ORM and simply replace the database-specific part with logic that saves entities using an API call. (and of course retrieves them using that same API). This way I can save my entities using roughly the same syntax:
// current way to save $entity in database:
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
// desired way to save $entity using REST API:
// (just an example, it doesn't have to be exactly like this)
$em = $this->getDoctrine()->getManager('rest');
$em->persist($entity);
$em->flush();
Note that I'm not trying to build my own API, I'm simply trying to communicate with a third party API in order to save my entities. I'm relatively new to Doctrine, but I'm liking it so far. I really like the idea of seperating the persistence logic from the entities, but so far I can't find out how I can use that to save them using an API.
There is an article in Symfony's documentation, describing how to work with multiple Entity Managers. I'm looking for a solution similar to this, but with an entity manager that enables me to use REST instead of the DB.
I've been trying to tweak Doctrine's ORM myself, but I only end up rewriting half their code because it (seems to be) too tightly coupled to the Database-specific logic. I might be doing something stupid of course.
So my question is, is there a way to replace / override the database-specific parts of Doctrine's ORM with custom ones? Without rewriting a lot of things that should be common for all persistence methods? Has it been done before? Or is it simply not possible because Doctrine is intended for use with a database and isn't flexible enough for other uses?
My own progress
CakePHP seems to be able to do this, by letting you define a custom DataSource. This way you can save your models using an SQL database, but also using an API, sessions, etc. I want to do roughly the same, but using Doctrine instead of CakePHP.
Update 1
The actual database queries seem to be executed by the
Doctrine\ORM\Persisters\BasicEntityPersister class. There are several other xxxPersister classes, to deal with different types of inheritance. It might be possible to replace the xxxPersister classes with our own, so we can replace the DB code with REST API code.
The persister objects are created within the getEntityPersister() method of the Doctrine\ORM\UnitOfWork class. The classnames are hardcoded so we need to override Doctrine\ORM\UnitOfWork if we want to use our own persisters.
Update 2
Doctrine\ORM\UnitOfWork seems to be hardcoded into Doctrine\ORM\EntityManager, so we need to override that one as well. However, this class seems to contain some database-specific parts. For instance, it's constructor requires a Doctrine\DBAL\Connection object as parameter. Perhaps it's better to create our own EntityManger (implementing the Doctrine\Common\Persistence\ObjectManager interface), as long as that doesn't take too much time / effort.
Update 3
The database-specific code for retrieving/loading/finding objects lives in the same class as the code for persisting / deleting etc: the Doctrine\ORM\Persisters\xxxPersister classes. So if we are able to replace them with our own, in order to persist objects, we can retrieve objects as well. When you call $entityRepository->findAll(), for instance, it will return $entityRepository->findBy(array()) (because findAll() is simply an alias for findBy(array())) which will run the following code:
$persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
return $persister->loadAll($criteria, $orderBy, $limit, $offset);
In other words, once we get EntityManager to create the right UnitOfWork and xxxPersister objects, we will be able to use the find methods in the EntityRepository.
Update 4
I discovered that a new feature is developed for Doctrine: custom persisters (also see this). This should make it easier to use a custom persister class. I don't know yet if it will enable us to create a non-DB persister, but it looks promising. However, the last updates were in August, so I'm not sure if it's still in active development.
You might use https://github.com/doctrine/rest to build a REST client, which talks to the target server. The essential part here is the mapping from entity (local) to REST API (target).
In short: Doctrine2 (local DB) -> Rest client (entity to rest mapping) -> Request (target server)
Doctrine/Rest provides also the other way around: a Doctrine Rest Server, to expose your local entities via REST (requests to your server). But thats not what you are looking for.
DoctrineRestDriver is exactly doing what you are looking for.
https://github.com/CircleOfNice/DoctrineRestDriver
Configure Doctrine:
doctrine:
dbal:
driver_class: "Circle\\DoctrineRestDriver\\Driver"
host: "http://www.your-url.com/api"
port: 80
user: "Circle"
password: "CantRenember"
Build entity:
/**
* This annotation marks the class as managed entity:
*
* #ORM\Entity
*
* You can either only use a resource name or the whole url of
* the resource to define your target. In the first case the target
* url will consist of the host, configured in your options and the
* given name. In the second one your argument is used as it is.
* Important: The resource name must begin with its protocol.
*
* #ORM\Table("products|http://www.yourSite.com/api/products")
*/
class Product {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=100)
*/
private $name;
public function getId() {
return $this->id;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
}
Let's assume we have used the value http://www.yourSite.com/api/products for the product entity's #Table annotation.
Controller:
<?php
namespace CircleBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\HttpFoundation\Response;
class UserController extends Controller {
/**
* Sends the following request to the API:
* POST http://www.yourSite.com/api/products HTTP/1.1
* {"name": "Circle"}
*
* Let's assume the API responded with:
* HTTP/1.1 200 OK
* {"id": 1, "name": "Circle"}
*
* Response body is "1"
*/
public function createAction() {
$em = $this->getDoctrine()->getManager();
$entity = new CircleBundle\Entity\Product();
$entity->setName('Circle');
$em->persist($entity);
$em->flush();
return new Response($entity->getId());
}
/**
* Sends the following request to the API by default:
* GET http://www.yourSite.com/api/products/1 HTTP/1.1
*
* which might respond with:
* HTTP/1.1 200 OK
* {"id": 1, "name": "Circle"}
*
* Response body is "Circle"
*/
public function readAction($id = 1) {
$em = $this->getDoctrine()->getManager();
$entity = $em->find('CircleBundle\Entity\Product', $id);
return new Response($entity->getName());
}
/**
* Sends the following request to the API:
* GET http://www.yourSite.com/api/products HTTP/1.1
*
* Example response:
* HTTP/1.1 200 OK
* [{"id": 1, "name": "Circle"}]
*
* Response body is "Circle"
*/
public function readAllAction() {
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('CircleBundle\Entity\Product')->findAll();
return new Response($entities->first()->getName());
}
/**
* After sending a GET request (readAction) it sends the following
* request to the API by default:
* PUT http://www.yourSite.com/api/products/1 HTTP/1.1
* {"name": "myName"}
*
* Let's assume the API responded the GET request with:
* HTTP/1.1 200 OK
* {"id": 1, "name": "Circle"}
*
* and the PUT request with:
* HTTP/1.1 200 OK
* {"id": 1, "name": "myName"}
*
* Then the response body is "myName"
*/
public function updateAction($id = 1) {
$em = $this->getDoctrine()->getManager();
$entity = $em->find('CircleBundle\Entity\Product', $id);
$entity->setName('myName');
$em->flush();
return new Response($entity->getName());
}
/**
* After sending a GET request (readAction) it sends the following
* request to the API by default:
* DELETE http://www.yourSite.com/api/products/1 HTTP/1.1
*
* If the response is:
* HTTP/1.1 204 No Content
*
* the response body is ""
*/
public function deleteAction($id = 1) {
$em = $this->getDoctrine()->getManager();
$entity = $em->find('CircleBundle\Entity\Product', $id);
$em->remove($entity);
$em->flush();
return new Response();
}
}
You can even use DQL or native queries.
As a ready-to-use solution wasn't available, I decided to write my own. I called it RAPL. It's heavily inspired by Doctrine's ORM (in fact, it uses many of the interfaces provided by Doctrine Common).
Using RAPL I can simply write a small YAML file to configure the mapping between my entities and the web service, allowing me to persist/retrieve entities using the custom EntityManager.
I think you are in not right way.
I'm not ready to dig into the documentation now, but I understand doctrine stack as:
ORM -> DQL (doctrine query language) ->dbal ->Some database sql
And point for implementation you feature in DBAL as custom database driver.
I think create common REST-Driver realy interesting feature and it will do easy integration with third-party services.
I'm not sure, but you can try to use lifecycle callback events for entities to perform persisting logic via REST.
I wanted to do a similar thing, so I built this library to help expose doctrine entities as RESTful resources. It has a fair amount of features, and allows you to define exactly what you want to have exposed via both pull (GET) and push (POST/PUT/PATCH) methods.
http://leedavis81.github.io/drest/
https://github.com/leedavis81/drest
Hope it helps

Categories