Testing Symfony custom maker (maker bundle) - php

I'm trying to make a custom maker with the Symfony make bundle.
The maker command looks like this:
<?php
namespace App\Maker;
use Doctrine\Common\Annotations\Annotation;
use Symfony\Bundle\MakerBundle\ConsoleStyle;
use Symfony\Bundle\MakerBundle\DependencyBuilder;
use Symfony\Bundle\MakerBundle\Generator;
use Symfony\Bundle\MakerBundle\InputConfiguration;
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
use Symfony\Bundle\MakerBundle\Str;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
final class MakeCustomEntity extends AbstractMaker
{
public static function getCommandName(): string
{
return 'make:custom-entity';
}
public static function getCommandDescription(): string
{
return 'Creates a new entity';
}
public function configureCommand(Command $command, InputConfiguration $inputConf)
{
$command
->addArgument('entity-class', InputArgument::OPTIONAL, sprintf('Choose a name for your entity class (e.g. <fg=yellow>%s</>)', Str::asClassName(Str::getRandomTerm())));
}
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
{
}
public function configureDependencies(DependencyBuilder $dependencies)
{
$dependencies->addClassDependency(
Annotation::class,
'doctrine/annotations'
);
}
}
So far so good, the custom maker shows up when listing all commands.
However I would like to write a test for this maker (inspired from the tests I have found on the bundles github):
<?php
namespace Tests\Maker;
use App\Maker\MakeCustomEntity;
use Symfony\Bundle\MakerBundle\Test\MakerTestCase;
use Symfony\Bundle\MakerBundle\Test\MakerTestDetails;
class MakeCustomEntityTest extends MakerTestCase
{
public function getTestDetails()
{
yield 'entity_full_custom_namespace' => [
MakerTestDetails::createTest(
$this->getMakerInstance(MakeCustomEntity::class),
[
// entity class name
'\App\Domain\Entity\Test\Test',
]
)
->assert(function (string $output, string $directory) {
$this->assertStringContainsString('created: src/Domain/Entity/Test/Test.php', $output);
}),
];
}
}
When I try to run this test I get the following warning and test doesn't fail even though it should:
The data provider specified for Tests\Maker\MakeCustomEntityTest::testExecute is invalid.
You have requested a non-existent service "maker.maker.make_custom_entity". Did you mean one of these: "maker.maker.make_authenticator",...
Is this the correct way to testing custom makers? What should I do to avoid this?

Related

Attempted to call function "yaml_parse_file" from the global namespace

I am new to Symfony, Facing problem while trying to run the cron job. I am really clueless, whats wrong here. It seems that I am trying to access some functions present in app/config/functions.php from the global namespace, But I can't figure out which namespace is it. Following is my code.
<?php
namespace App\Command;
use App\Services\Upcontent\Upcontent;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class UpcontentRefreshCommand extends Command
{
protected static $defaultName = 'app:upcontent-refresh';
private $upcontent;
public function __construct(Upcontent $upcontent)
{
$this->upcontent = $upcontent;
parent::__construct();
}
protected function configure()
{
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln([
'',
'=================',
'Upcontent Refresh',
'=================',
'',
]);
$output->writeln('Clearing Cache...');
clear_cache();
$output->writeln('Cache Cleared');
$output->writeln('Refreshing Sports Topic...');
$output->writeln('Loading, be patient...');
$sports = $this->upcontent->getTopic('########');
$output->writeln([
'',
'=====================',
'End Upcontent Refresh',
'=====================',
'',
]);
}
}
?>
The error occur when I run, php72 bin/console app:upcontent-refresh Please help. Thanks in advance.
You will probably have to update the "autoload"-section to make sure that your custom functions.php is loaded.
{
"autoload": {
"psr-4" {
"App\\": "src/"
},
"files": ["app/config/functions.php"]
}
}
You might also want to refactor that file to instead move the functions into some kind of service-class, e.g. like this:
# src/Yaml/Parser.php
<?php
namespace App\Yaml;
class Parser
{
public function parseFile(string $fileName)
{
// The logic from your yaml_parse_file() inside your functions.php
}
}
Then in your command (or wherever you need your custom yaml parsing) inject the service:
use App\Yaml\Parser;
class MyService
{
private $yamlParser;
public function __construct(Parser $yamlParser)
{
$this->yamlParser = $yamlParser;
}
// ...
public function something()
{
$this->yamlParser->parseFile($filename);
}
}
Since Symfony provides a Yaml-component, you might even want to use that instead.

Call built-in filter in custom Twig filter

When setting up a custom Twig filter (see https://symfony.com/doc/current/templating/twig_extension.html ), how can I call an existing Twig filter in my custom function?
https://stackoverflow.com/a/41551944/1668200 suggests parent::dateFilter($timestamp, $format); but that isn't working:
Attempted to call an undefined method named "dateFilter" of class "Twig_Extension".
The example you've linked is actually incorrect. The proper way would be like this,
class DateEmptyIfNull extends Twig_Extension // or: extends AbstractExtension
{
public function getFilters()
{
return array(
new TwigFilter('date', [ $this, 'dateFilter'], ['needs_environment' => true, ]),
);
}
public function dateFilter(Twig_Environment $env, $timestamp, $format = 'F j, Y H:i')
{
return $timestamp === null ? '' : twig_date_format_filter($env, $timestamp, $format);
}
}
not all twig extensions have their dedicated gloal php function (mainly true for 3rd party twig extensions i think) then the best way if you use the symfony framework is to use autowiring as twig functions are public and can be called via php.
in my case i wanted to create a asset function which downloads remote files to the local filesystem and then returns the local path which can be used with the imagine_filter filter (which only easily works with local images)
add the twig class to services yaml to allow autowiring if necessary.
services.yaml
services:
Symfony\Bridge\Twig\Extension\AssetExtension: '#twig.extension.assets'
create your own filter filter/function with a constructor where the other extension can be incjected.
<?php
declare(strict_types=1);
namespace App\Twig;
use Symfony\Bridge\Twig\Extension\AssetExtension;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class ImageAssetExtension extends AbstractExtension
{
private AssetExtension $assetExtension;
public function __construct(
AssetExtension $assetExtension,
) {
$this->assetExtension = $assetExtension;
}
public function getFunctions(): array
{
return [
new TwigFunction('asset_image', [$this, 'assetImage']),
];
}
public function assetImage(string $path, string $packageName = null): string
{
// do something more here (like downloading a remote asset and returning the local path)
return $this->assetExtension->getAssetUrl($path, $packageName);
}
}
The method dateFilter() belongs to class DateEmptyIfNull. In this case your class must extend this class

Behavior-Driven-Development is failing my expectations with PHPSpec

I have the following class generated through PHPSpec:
class Consumer
{
public function __construct($accesskey, $accessToken)
{
// TODO: write logic here
}
}
When I test the constructor I get an error that it is missing Argument 1. Below is how I have written the behavior:
namespace spec\Zizy\Aggregator\Context;
use Zizy\Aggregator\Context\Contract\ContextContractInterface;
use Zizy\Aggregator\Context\Consumer;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ConsumerSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->beConstructedWith( md5('samplekey'), md5('sampletoken') );
$this->shouldHaveType(Consumer::class);
}
/**
* This spec describes how we would access our consumer directry
*/
public function it_gets_access_token()
{
$this->getAccessToken()->shouldReturn(md5('sampletoken'));
}
}
Below is the error I get when running PHPSpec.
Zizy\Aggregator\Context\Consumer 21 - it gets access token
warning: Missing argument 1 for Zizy\Aggregator\Context\Consumer::__construct() in C:\wamp64\www\spikes\src\Context\Consumer.php line 7
I have also tried to test my consumer through an interface but PHPSpec keeps telling me that it cannot find the interface but in a class context thus offer me an opportunity to create the class meanwhile it should actually be an interface.
How can I also write code through interfaces with PHPSpec?
You'll need to specify the constructor arguments for every example case. If you find that a bit too laborious, you can use let to make preparations for before each example is run. For your case, something like this should work:
namespace spec\Zizy\Aggregator\Context;
use Zizy\Aggregator\Context\Contract\ContextContractInterface;
use Zizy\Aggregator\Context\Consumer;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ConsumerSpec extends ObjectBehavior
{
function let()
{
$this->beConstructedWith( md5('samplekey'), md5('sampletoken') );
}
function it_is_initializable()
{
$this->shouldHaveType(Consumer::class);
}
/**
* This spec describes how we would access our consumer directry
*/
public function it_gets_access_token()
{
$this->getAccessToken()->shouldReturn(md5('sampletoken'));
}
}

Unable to use helper in controller of laravel app

I'm building an application, now i'm created a helper
class Students{
public static function return_student_names()
{
$_only_student_first_name = array('a','b','c');
return $_only_student_first_name;
}
}
now i'm unable to do something like this in controller
namespace App\Http\Controllers;
class WelcomeController extends Controller
{
public function index()
{
return view('student/homepage');
}
public function StudentData($first_name = null)
{
/* ********** unable to perform this action *********/
$students = Student::return_student_names();
/* ********** unable to perform this action *********/
}
}
this is my helper service provider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class HelperServiceProvider extends ServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
foreach(glob(app_path().'/Helpers/*.php') as $filename){
require_once($filename);
}
}
}
i event added it as an alias in config/app.php file
'Student' => App\Helpers\Students::class,
Try putting use App\Helpers\Student; at the top of your controller beneath the namespace delcaration:
namespace App\Http\Controllers;
use App\Helpers\Student;
class WelcomeController extends Controller
{
// ...
Look more into PHP namespaces and how they are used, I believe you may have a deficient understanding about them. Their only purpose is to make so you can name and use two classes with the same name (e.g. App\Helpers\Student vs maybe App\Models\Student). If you needed to use both of those classes inside of the same source file, you can alias one of them like this:
use App\Helpers\Student;
use App\Models\Student as StudentModel;
// Will create an instance of App\Helpers\Student
$student = new Student();
// Will create an instance of App\Models\Student
$student2 = new StudentModel();
You do not need to have a service provider for this, just the normal language features. What you would need a service provider for is if you wanted to defer the construction of your Student object to the IoC:
public function register()
{
$app->bind('App\Helpers\Student', function() {
return new \App\Helpers\Student;
});
}
// ...
$student = app()->make('App\Helpers\Student');
You should never have to include or require a class file in laravel because that is one of the functions that composer provides.
You do not need a service provider to make it works. Just lets the Students class as you did:
class Students{
public static function return_student_names()
{
$_only_student_first_name = array('a','b','c');
return $_only_student_first_name;
}
}
all its methods should be static
You added the Facade correctly:
'Student' => App\Helpers\Students::class,
Finally, looks like your problem is caused by forgetting a backslash at facade name. Uses \Students instead of Students:
public function StudentData($first_name = null)
{
$students = \Student::return_student_names();
}
When using a facade, it is not necessary makes nay include, the facades were made to avoid complex includes in everywhere.

How do I use a misc function in Symfony2?

Where do I store misc functions? How do I use them? Should it be a DependencyInjection? Should it just be a class and do I do something like use Acme\Bundle\AcmeBundle\Misc\ClientIPChecker?
Say I have a function:
<?php
class ClientIPChecker {
public static function isLocal(Request $request){
return in_array('127.0.0.1', $request->getClientIp())
}
}
And I want to use this function in two controllers. How do I do this in Symfony2?
If you have a set of consistent function put them in a class/service. If functions do different things put them in the appropriate class/service. In this particular case, I'll go for either custom Request or custom Controller (probably the latter, avoding messing app.php or app_dev.php).
With custom controller this doesn't work:
// Automatic binding of $request parameter
public function indexAction(Request $request)
{
// Won't work with custom controller
if ($request->isLocal)) {
// ...
}
// You have to do
if ($this->getRequest()->isLocal()) {
// stuff
}
}
Option 1: extend Symfony Request
namespace My\HttpFoundation;
use Symfony\Component\HttpFoundation\Request as BaseRequest;
class Request extends BaseRequest
{
public function isLocal()
{
return in_array('127.0.0.1', $this->getClientIp());
}
}
Then in web/app.php and web/app_dev.php modify:
use Symfony\Component\HttpFoundation\Request;
to be:
use My\HttpFoundation\Request;
Option 2: create a BaseAbstractController and use it instead of Symfony controller
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
abstract class BaseAbstractController extends Controller
{
public function isRequestLocal()
{
return in_array('127.0.0.1', $this->getRequest()->getClientIp())
}
}
Option 3: custom service as explained here

Categories