Jump to content

NotionCommotion

Members
  • Content Count

    1,854
  • Joined

  • Last visited

  • Days Won

    8

NotionCommotion last won the day on January 3

NotionCommotion had the most liked content!

Community Reputation

29 Good

About NotionCommotion

  • Rank
    Prolific Member

Contact Methods

  • Website URL
    http://

Profile Information

  • Gender
    Not Telling

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. NotionCommotion

    Service to support different types of resources

    Need the service use some if/then logic to look up the information and determine the data source, or typically is this performed elsewhere? Inject the data into the chart entity? Also, my issues are not related to populating a chart with data and displaying that chart, but creating and modifying the metadata which defines those charts such as adding a series, etc. Actually, I expect you know that. What you might not know is the remote chart is not just reaching out to some other server to get data, but reaching out to some other server to instruct it to start trending some physical variable (or how to trend it) so that it can be used in a future chart. I should have named the method something more descriptive. What I could (and will) do is decouple this from the general chart and put it in maybe the ChartNode where a bar, pie, etc chart can have either a LocalChartNode or a RemoteChartNode. But the need still remains to determine whether the given chartNode is remote or not and if so perform some cURL query. And I think I need to push that functionality down and abstracting it from the service, no? But how to do so is still a question. if($chart->isType('remote')) { try{ $this->remoteConnection()->tellRemoteServerToTrendPhysicalVariable($id, $params); } catch (RemoteConnectionException $e) { //clean up. throw new ChartServiceException($e->getMsg()); } }
  2. NotionCommotion

    Service to support different types of resources

    Agree I think, but I am not certain where to start. My partial implementation I showed for individual services for each chart type was an attempt to do so. All services will implement an interface with methods updateChart(), deleteChart(), etc, and only the applicable service will perform the data scope so that the initiator (router) doesn't need to be aware of these details. Or by injecting (or even hard coding) a separate class in the applicable repository, the initiator (service in this case) will not need to be aware of these details. Is this what you mean by "abstract out"? I don't, however, think I am going down the right path. Can you elaborate? Thanks
  3. NotionCommotion

    Service to support different types of resources

    Thanks requinix. I feel I have come a long way thanks to the good advice I have received on this forum. From your response, I assume I should not attempt to implement my sub-service idea, true? Doesn't hurt my feelings as I would rather not. What if I had to do something different for only a specific type of chart. For instance, I have these remote charts that need to perform a cURL request to another server. Before making this request, I want to verify everything locally, and if all good, perform the remote update, and then save locally. I suppose I can do something like the following, but it seems so ugly. Or I can perform this scope in the specific RemoteChart repository, but I am not sure how to get the applicable objects in the repository other than hard coding $remoteConnection=new RemoteConnection($url, $port, $etc); in the repository. <?php class ChartService { public function updateChart(int $id, array $params):Chart { $chart=$this->read($id); //Validate data $repo=$this->em->getRepository(get_class($chart)); $repo->update($chart, $params); if($chart->isType('remote')) { try{ $rsp = $this->remoteConnection()->updateChart($id, $params); //Maybe do something with $rsp } catch (RemoteConnectionException $e) { //clean up. throw new ChartServiceException($e->getMsg()); } } $this->em->persist($chart); $this->em->flush(); return $chart; } }
  4. I've asked this question before but still haven't really figured out how best to implement it, and have found myself hitting another roadblock. I have multiple types of charts (i.e. bar, pie, etc), and various endpoints to access them. I like to keep my routing script uncluttered, and feel the following does so. $app->get('/chart', function (Request $request, Response $response) { //List of chart with optional filtering $this->chartService->index($request->getQueryParams()); return $this->chartResponder->index($response, $index); }); $app->post('/chart', function (Request $request, Response $response) { //Create a new chart of type (i.e. bar, pie, etc) specified by type parameter $chart=$this->chartService->create($request->getParsedBody()); return $this->chartResponder->create($response, $chart); }); $app->get('/chart/{id:[0-9]+}', function (Request $request, Response $response, $args) { //View chart of given ID $chart=$this->chartService->detail((int)$args['id']); return $this->chartResponder->delete($response, $chart); }); $app->delete('/chart/{id:[0-9]+}', function (Request $request, Response $response, $args) { //Delete chart of given ID $this->chartService->delete((int)$args['id']); return $this->chartResponder->delete($response, null); }); $app->post('/chart/{id:[0-9]+}/series', function (Request $request, Response $response, $args) { //Add a new series to the collection for chart with given ID $chart=$this->chartService->addSeries((int)$args['id'], $request->getParsedBody()); return $this->chartResponder->update($response, $chart); }); $app->put('/chart/{id:[0-9]+}/series/{seriesPosition:[0-9]+}', function (Request $request, Response $response, $args) { //Modify series of given position in the collection for chart with given ID $chart=$this->chartService->updateSeries((int)$args['id'], (int)$args['seriesPosition'], $request->getParsedBody()); return $this->chartResponder->update($response, $chart); }); //More endpoints for chart of given ID... I then created the following service to support the endpoints. Off topic, but is this a service or a controller? My index() method is chart type agnostic. My create() method needs a means to determine what type of chart to create, and does so using the received chart "type" passed in the body to get the applicable repository. All the other methods receive the chart ID in the URL path and use it to first get the chart entity and then get the applicable repository based on the object. All seems good! <?php namespace NotionCommotion\ChartBuilder\Service; use NotionCommotion\ChartBuilder\Entity\Chart; class ChartService { protected $em; public function __construct(\Doctrine\ORM\EntityManager $em) { $this->em = $em; } public function index(array $params=[]):array { return $this->em->getRepository(Chart::class)->index($params); } public function create(array $params):Chart { //Get the specific repo based on $params['type]. Not perfect, but good enough $discriminatorMap=$this->em->getClassMetadata(Chart::class)->discriminatorMap; $repo = $this->em->getRepository($discriminatorMap[$params['type']]); //Validate data $chart=$repo->create($params); $this->em->persist($chart); $this->em->flush(); return $chart; } public function read(int $id):Chart { return $this->em->getRepository(Chart::class)->find($id); } public function delete(int $id):void { $this->em->remove($this->read($id)); $this->em->flush(); } public function addSeries(int $idPublic, array $id):Chart { $chart=$this->read($id); //Validate data $repo=$this->em->getRepository(get_class($chart)); $repo->addSeries($chart, $params); $this->em->persist($chart); $this->em->flush(); return $chart; } public function updateSeries(int $id, int $position, array $params):Chart { $chart=$this->read($id); $series=$chart->getSeries(); $seriesNode=$series->offsetGet($position); //Validate data $repo=$this->em->getRepository(get_class($seriesNode)); $repo->update($seriesNode, $params); $this->em->persist($chart); $this->em->flush(); return $chart; } } Until... I find myself needing to put non-database related functionality in the repository and violating the single responsibility principle. I am not a complete purist and might be willing to do so, however, there does not appear to be a clean way to inject dependencies in a Doctrine repository. One thought I had was to create specialized services maybe as follows: $c['chartService'] = function ($c) { return new ChartService( $c[EntityManager::class], [ 'bar'=>function ($c) {return new BarChartService($c[EntityManager::class]);}, 'pie'=>function ($c) {return new PieChartService($c[EntityManager::class], $c['someOtherObject']);}, //add more types... ] ); }; class ChartService { protected $em, $subServices=[]; public function __construct(\Doctrine\ORM\EntityManager $em, $subServices) { $this->em = $em; $this->subServices = $subServices; } public function index(array $params=[]):array {/* no change */} public function create(array $params):Chart {/* maybe no change */} public function __call($name, $args) { $chart=$this->em->getRepository(Chart::class)->find($args[0]); $subservice=$this->getSubservice($chart); $args[0]=$chart; return $subservice->$name(...$args); } } abstract class AbstractSpecificChartService { protected $em; public function __construct(\Doctrine\ORM\EntityManager $em) { $this->em = $em; } public function __call($name, $args) { throw new \Exception("Method $name not supported"); } protected function getSubservice(Chart $chart):self { //Haven't figured out but can do so if needed. } public function delete(Chart $chart):void { //Include methods common to all charts here $this->em->remove($chart); $this->em->flush(); } protected function helperMethods($foo) { //If necessary. } } class BarChartService extends AbstractSpecificChartService { //Override __construct if necessary public function updateSeries(Chart $chart, int $position, array $params):Chart { //Note that Chart and not $id is passed. //implement code as needed return $chart; } } While this will provide some flexibility, I've been stung more than once using inheritance when injection should be used, and am worried it will lead to no good. But what would I inject? I previously suggested injecting a BarChart into a Chart and received a response that doing so is nonsensible. So, after this long story, how should I implement this? Thank you
  5. The content of the email can influence whether it will get through the recipients filter. For instance, I wrote and manage a bid solicitation application which sends emails out to one to around ten individuals per transaction, and all at once, emails mysteriously started going to the recipient's junk folder. I later discovered that in the body of the emails, there was a HTML link using HTTPS, however, the destination of the link no longer supported HTTPS but only HTTP which resulted in being blocked by some filters. Also, the quality of your email host can influence whether tagged as spam.
  6. To expand on requinix's response: $options = ['Orange', 'Apple', 'Pear']; $select=mt_rand(0,2); $selected=$options[$select];
  7. Yep, all good now. Thanks again.
  8. Thanks, After trying to create a brand new project with no real packages, got the same error and figured it had to something like that.
  9. I am getting the following error when using composer: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? Just started. I am pretty sure composer.json wasn't changed. journalctl doesn't show anything. Maybe Doctrine related, however, nothing seems to help. Any ideas? [michael@devserver www]$ php -v PHP 7.3.4 (cli) (built: Apr 2 2019 13:48:50) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.3.4, Copyright (c) 1998-2018 Zend Technologies with DBG v9.1.9, (C) 2000,2018, by Dmitri Dmitrienko [michael@devserver www]$ composer -V Composer version 1.4.2 2017-05-17 08:17:52 [michael@devserver www]$ yum info composer Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * base: mirror.us.oneandone.net * epel: mirror.rnet.missouri.edu * extras: mirror.us.oneandone.net * remi-php73: mirror.bebout.net * remi-safe: mirror.bebout.net * updates: mirror.us.oneandone.net Installed Packages Name : composer Arch : noarch Version : 1.8.4 Release : 1.el7 Size : 1.8 M Repo : installed From repo : epel Summary : Dependency Manager for PHP URL : https://getcomposer.org/ License : MIT Description : Composer helps you declare, manage and install dependencies of PHP projects, : ensuring you have the right stack everywhere. : : Documentation: https://getcomposer.org/doc/ [michael@devserver www]$ composer update Loading composer repositories with package information Updating dependencies (including require-dev) [ErrorException] "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? update [--prefer-source] [--prefer-dist] [--dry-run] [--dev] [--no-dev] [--lock] [--no-custom-installers] [--no-autoloader] [--no-scripts] [--no-progress] [--no-suggest] [--with-dependencies] [-v|vv|vvv|--verbose] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [-i|--interactive] [--root-reqs] [--] [<packages>]... [michael@devserver www]$
  10. NotionCommotion

    Name/value pairs or key/value properties for REST API update

    Ah, that is when I use PATCH! I've never used the method, but was actually planning on using this approach but as PUT. I even came up with the following to use in concert with xeditable. function conv2obj(keys, value, object) { keys=path.split('.'); object=object?object:{}; var last = keys.pop(); keys.reduce((o, k) => o[k] = o[k] || {}, object)[last] = value; return object; } var o=convert('option.plotOptions.solidgauge.dataLabels.enable', true); //{"option":{"plotOptions":{"solidgauge":{"dataLabels":{"enable":true}}}}} Shouldn't your second example us PUT since it is idempotent? It doesn't replace the entire /some/resource/123 resource, but it does replace the entire /some/resource/123/toggle/label/solidgauge/enabled resource.
  11. NotionCommotion

    Help with substituting web script code

    I read your original post a couple times, and not sure what you are asking. Maybe I needed to follow your earlier posts for reference? Regardless, if you are trying to substitute one set of code with another, you need to figuratively draw a circle around it and understand (and even write down) how it interfaces to the remaining code, and then make sure your new code interfaces to the remaining code the same way.
  12. NotionCommotion

    Name/value pairs or key/value properties for REST API update

    Just to make sure I was correctly communicating on my previous post, to change option.plotOptions.solidgauge.dataLabels.enabled from false to true, I would do one of the following requests: PUT /some/resource/123/option/plotOptions/solidgauge/dataLabels body: {enabled: true} //or PUT /some/resource/123/option/plotOptions/solidgauge/dataLabels/enabled body: true //If this is an acceptable approach, which request should be used? and then get resource 123, get the option property, and finally use [plotOptions, solidgauge, dataLabels, enabled] to target the specific property. Still too weird? And to make sure I understand you, stick with the standard where the entire option JSON string is in the body, right?
  13. NotionCommotion

    Name/value pairs or key/value properties for REST API update

    Before moving on... The following seems clean enough: //PUT /some/resource/123 body: {"description": "Some new description", "name": "new name" ...} $app->get('/some/resource/{id:[0-9]+}', function (Request $request, Response $response, $args) { $resource=$this->getResource($args['id']); $params=$request->getParsedBody(); foreach($params as $name=>$value) { $resource->$name=$value; //but implement more securely } }); If I only wanted to update a single property (i.e. {"description": "Some new description"} ), the same server code works. But I can also change the client request and do something like the following. //PUT /some/resource/123/description body: Some new description $app->get('/some/resource/{id:[0-9]+}/{property}', function (Request $request, Response $response, $args) { $resource=$this->getResource($args['id']); $value=$request->getBody(); $resource->$args['property']=$value; //but implement more securely }); Under normal circumstances, probably doesn't make sense. Agree? What if I wasn't updating some value stored in the DB but some "option" object which is stored as MySQL type JSON and wanted to keep it more flexible? { "title": { "text": "Activity", "style": { "fontSize": "24px" } }, "tooltip": { "borderWidth": 0, "backgroundColor": "none", "shadow": false, "style": { "fontSize": "16px" }, "pointFormat": "{series.name}<br><span style=\"font-size:2em; color: {point.color}; font-weight: bold\">{point.y}%</span>" }, "pane": { "startAngle": 0, "endAngle": 360, "background": [{ "outerRadius": "112%", "innerRadius": "88%", "backgroundColor": "rgba(124,181,236,0.3)", "borderWidth": 0 }, { "outerRadius": "87%", "innerRadius": "63%", "backgroundColor": "rgba(67,67,72,0.3)", "borderWidth": 0 }, { "outerRadius": "62%", "innerRadius": "38%", "backgroundColor": "rgba(144,237,125,0.3)", "borderWidth": 0 } ] }, "yAxis": { "min": 0, "max": 100, "lineWidth": 0, "tickPositions": [] }, "plotOptions": { "solidgauge": { "dataLabels": { "enabled": false }, "linecap": "round", "stickyTracking": false, "rounded": true } } } I was thinking of something like the following to implement: //PUT /some/resource/123/option/foo/bar/someproperty body: TBD $app->get('/some/resource/{id:[0-9]+}/option/{subproperties:.*}', function (Request $request, Response $response, $args) { $resource=$this->getResource($args['id']); $properties=explode('/', $args['subproperties']); //Update specific property in $resource->option using $properties }); For this it kind of makes sense to target the specific property, but this is contrary to the earlier approach. Or maybe I should keep with the {name: value} approach?
  14. NotionCommotion

    Name/value pairs or key/value properties for REST API update

    Gotcha! I will go with the second.
  15. I implemented an endpoint which receives name/value pairs: PUT someresource/123 {"name": "description", "value": "Some new description"} I think I made a mistake, and should have implemented it as: PUT someresource/123 {"description": "Some new description"} The reason I think so is updating multiple properties is only (easily) possible using the later: PUT someresource/123 {"description": "Some new description", "otherProperty": "bla bla bla"} Assuming I am not using some 3rd party client library which only works with name/value pairs, any compelling reason why one shouldn't default to the later approach with key/values?
×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.