Is it possible to override a Symfony 1.4 CLI command?
More specifically, I was wondering if it's possible to override this command:
php symfony doctrine:build-schema
What I want to do is to add a new option in the database.yml file for each connection I find in it.
The option I want to add is a package option
So, an hypothetical connection could be:
all:
doctrine:
class: sfDoctrineDatabase
package: myPackageOption
param:
dsn: 'mysql:host=localhost;dbname=my_db_name'
username: db_user
password: db_password
If it would be possible, where can i find the code to override?
I suggest you to use some shell script that pre-generate the databses.yml and then auto-invoque the php symfony doctrine:build-schema. Something like:
build.sh, in project root folder:
#!/bin/bash
cp config/databases_1.yml config/databases.yml
php symfony doctrine:build
then, type ./build.sh (after added execution permissions) in your console.
The copy/replace of multiple databases_xxx.yml it's the easiest example. But you can do any processing you want.
If you don't know about shell scripting, you can do the file modification even with a php script, so your build.sh should looks like:
#!/bin/bash
php pregenerate_databases.php
php symfony doctrine:build
I'm trying to override the task but I can't make it work, but:
You can create your own task that inherits the doctrine task and do your stuff:
in lib/task add sfDoctrineBuildSchemaCustomTask.class.php:
class sfDoctrineBuildSchemaCustomTask extends sfDoctrineBuildSchemaTask
{
/**
* #see sfTask
*/
protected function configure()
{
$this->addOptions(array(
new sfCommandOption('application', null, sfCommandOption::PARAMETER_OPTIONAL, 'The application name', true),
new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
));
$this->namespace = 'doctrine';
$this->name = 'build-schema-custom';
$this->briefDescription = 'Creates a schema from an existing database';
$this->detailedDescription = <<<EOF
The [doctrine:build-schema|INFO] task introspects a database to create a schema:
[./symfony doctrine:build-schema|INFO]
The task creates a yml file in [config/doctrine|COMMENT]
EOF;
}
/**
* #see sfTask
*/
protected function execute($arguments = array(), $options = array())
{
// do your stuff before original call
parent::execute($arguments,$options);
// do your stuff after original call
}
}
Then, you can call php symfony doctrine:build-schema-custom, and go!
Or, maybe, you can edit the original task located in lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/task/sfDoctrineBuildSchemaTask.class.php
Related
I have a set of tests that pass locally, but once I push to Github, the tests fail. The Github Action seems like a black box to me, and I'm unsure of even how to troubleshoot this. I'm using in-memory sqlite both locally and in my github action workflow.
I'm inserting some data for my tests, and at the beginning of these seeder classes, I'm temporarily disabling foreign keys to allow me to truncate the tables:
Schema::disableForeignKeyConstraints();
QuickbooksTransaction::truncate();
Schema::enableForeignKeyConstraints();
Here's the start of my test class that's producing the errors:
class FindMatchesTest extends TestCase
{
use RefreshDatabase, WithFaker;
protected $user;
protected $client;
protected $location;
protected $statement;
public function setup(): void
{
parent::setUp();
$this->setupFaker();
$this->seed();
$this->user = User::factory()->create();
$this->client = Client::factory()->create();
$this->location = Location::factory()->create();
$this->statement = Statement::factory()->create();
$this->seed(StatementTransactionSeeder::class); //truncates and populates a table
$this->seed(TransactionSeeder::class); //truncates and populates a table
$action = new FindMatches($this->statement, true);
$action->handle();
}
this is the first of 3 tests that are failing. It doesn't matter which assertion I put first, they all fail:
public function test_right_matching_invoice_nbr_produces_match()
{
$row = StatementTransaction::where('invoice_nbr', '999999654321')->first();
$qbRow = Transaction::where('invoice_nbr', '888888654321')->first();
$this->assertNotEmpty($row->transaction_id);
$this->assertEquals(1, $row->has_partial_match);
$this->assertEquals(0, $row->has_match);
$this->assertEquals(0, $row->has_identified_match);
$this->assertEquals($row->transaction_id, $qbRow->id);
$this->assertGreaterThanOrEqual(Config::get('match_threshold'), $row->invoice_nbr_match_confidence);
}
If I leave the order that's above, I get Failed asserting that 7 matches expected 8.
If I switch the order up and put the next row first, I get Failed asserting that 1 matches expected 0.
It's almost like it's seeeing different data than what I'm seeing locally, but that doesn't seem possible if it's running it the same way as it is locally.
Could there be a difference in how PHPUnit would run locally and how it would run in Github actions? Is there a way to see the data it's seeing when it runs so I can begin to troubleshoot this?
Here is my Github Action config file:
name: LaravelTest
on:
push:
branches: [ test ]
jobs:
laravel_tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#main
- name: Copy .env
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
- name: Install Dependencies
run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress
- name: Generate key
run: php artisan key:generate
- name: Directory Permissions
run: chmod -R 777 storage bootstrap/cache
- name: Create Database
run: |
mkdir -p database
touch database/database.sqlite
- name: Compile assets
run: |
npm install
npm run production
- name: Execute tests (Unit and Feature tests) via PHPUnit
env:
DB_CONNECTION: sqlite
DB_DATABASE: database/database.sqlite
CACHE_DRIVER: array
SESSION_DRIVER: array
QUEUE_DRIVER: sync
run: vendor/bin/phpunit
forge_deploy:
runs-on: ubuntu-latest
needs: laravel_tests
steps:
- name: Make Get Request
uses: satak/webrequest-action#master
with:
url: ${{ secrets.TEST_DEPLOY_URL }}
method: GET
EDIT: adding the seeder classes. Both classes are close to identical, with just a bunch of insert statements to create the dummy records for testing.
class TransactionSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
Schema::disableForeignKeyConstraints();
Transaction::truncate();
Schema::enableForeignKeyConstraints();
$id = Statement::first()->id;
$inv = Helper::cleanInvoiceNumber('888888654321');
$date = Carbon::now()->startOfMonth()->format('Y-m-d');
DB::table('transactions')->insert([
'statement_id' => $id,
'transaction_type_id' => 1,
'transaction_date' => $date,
'invoice_nbr' => '888888654321',
'invoice_nbr_clean' => $inv,
'original_amount' => '16100',
'balance' => '16100',
'row_hash' => Helper::makeHash([
$id,
1,
$inv,
$date,
'16100'
])
]);
//...more inserts like above...
}
class StatementTransactionSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
Schema::disableForeignKeyConstraints();
StatementTransaction::truncate();
Schema::enableForeignKeyConstraints();
$id = Statement::first()->id;
$inv = Helper::cleanInvoiceNumber('999999654321');
$date = Carbon::now()->startOfMonth()->format('Y-m-d');
DB::table('statement_transactions')->insert([
'statement_id' => $id,
'transaction_type_id' => 1,
'transaction_date' => $date,
'invoice_nbr' => '999999654321',
'invoice_nbr_clean' => $inv,
'po_nbr' => 'Right 6 match',
'original_amount' => '16100',
'balance' => '16100',
'row_hash' => Helper::makeHash([
$id,
1,
$inv,
$date,
'16100'
])
]);
//...more inserts like above...
}
Clarification requested in comment:
When I say switch the order, I just mean that the first assertion always fails in this test, regardless of the order of the assert statements. So this:
$this->assertNotEmpty($row->transaction_id);
$this->assertEquals(1, $row->has_partial_match);
or this:
$this->assertEquals(1, $row->has_partial_match);
$this->assertNotEmpty($row->transaction_id);
will both fail on the first assert statement it hits. This, combined with the error message it spits out, leads me to believe that the data isn't the same on the server as it is locally for some reason.
UPDATE
Based on feedback in the comments, I updated the line to get the statement with this: $id = Statement::orderBy('id', 'desc')->first()->id;
Now all the tests in this class are failing with the message: You requested 1 items, but there are only 0 items available..
I have no idea what this means. Currently wading through google results.
UPDATE 2
Ok that was wrong. I think I incorrectly removed a line of code for a factory that wasn't needed in the test, but was a dependency for another factory.
I'm back at square one.
UPDATE 3
In an effort to not spend my entire life on this, I'm going back to the drawing board and eliminating the seeder classes and just inserting the records I need for the specific test inside the test itself. Hoping this resolves the issue so I don't have to jump off a cliff in despair.
I'm following the symfony docs (https://symfony.com/doc/current/testing/doctrine.html) trying to test my repository classes against my MySQL database.
The relevant code part is this:
class ProductRepositoryTest extends KernelTestCase
{
/**
* #var \Doctrine\ORM\EntityManager
*/
private $entityManager;
/**
* {#inheritDoc}
*/
protected function setUp()
{
$kernel = self::bootKernel();
$this->entityManager = $kernel->getContainer()
->get('doctrine')
->getManager();
}
public function testSearchByCategoryName()
{
$products = $this->entityManager
->getRepository(Product::class)
->searchByCategoryName('foo')
;
$this->assertCount(1, $products);
}
/**
* {#inheritDoc}
*/
protected function tearDown()
{
parent::tearDown();
$this->entityManager->close();
$this->entityManager = null; // avoid memory leaks
}
}
When I execute the testclass with PhpUnit I get a Doctrine\DBAL\Exception\ConnectionException with the message: "An exception occurred in driver: SQLSTATE[HY000] [1045] Access denied for user 'root'#'localhost' (using password: NO)".
I stored my database connection data in the DATABASE_URL in the .env file and the connection works fine when executing: bin/console doctrine:migrations:migrate
But it seems this configuration is not used for testing (because 'NO' is not my password in the .env file). Do I have to store the connection configuration in another file for testing? And if yes, where do I have to store the configuration, so that it gets used in my testcase?
Thanks in advance for any help!
The .env file is only used in dev environment. If you're in prod or in test, it's not even read: https://symfony.com/doc/current/configuration.html#the-env-file-environment-variables. It's clear in some parts of the doc, but less in some others, which can be confusing.
In your case, you can check your config folder, more specifically probably under config/packages/doctrine.yaml and config/packages/parameters.yaml, and see how the DATABASE_URL is used (when it exists). From there, you can either hardcode that URL in config files specific to the test environment (creating a test subfolder under config/packages for instance), or you can give the proper DATABASE_URL environment variable to phpunit "manually" by using APP_ENV=... && bin/phpunit ....
Here's what a solution would look like in the first scenario :
# config/packages/test/doctrine.yaml
doctrine:
dbal:
url: 'mysql://db_user:db_password#127.0.0.1:3306/db_name'
Lumen 5.4, MySql & Docker. I have following variables in global env
$ printenv
DB_HOST=127.0.0.1
DB_DATABASE=database
etc
.env in my project the are present also, but they have different values.
If I type in tinker env('DB_HOST'), it prints value from the global environment, but when application runs, it takes from the specified .env file. I think the problem exists within following function in Laravel\Lumen\Application :
/**
* Load a configuration file into the application.
*
* #param string $name
* #return void
*/
public function configure($name)
{
if (isset($this->loadedConfigurations[$name])) {
return;
}
$this->loadedConfigurations[$name] = true;
$path = $this->getConfigurationPath($name);
if ($path) {
$this->make('config')->set($name, require $path);
}
}
How to override those values or make it to avoid those conditions: isset($this->loadedConfigurations[$name]) ?
I still think that, regarding my comment, the answer remains the same. If you wish to utilize the docker environment variables as opposed to your local .env variables, then the config directory is still the way to go. In this case, it looks like you wish to target the database host. So let's do that:
In your config/database.php file, change the following:
'mysql' => [
//...
'host' => getenv('DB_HOST') ?: env('DB_HOST', 'defaultvalue')
]
Then only make reference to the host through the config file.
config("database.mysql.host");
You will get the ENV from your docker container if it exists, otherwise you will get the DB_HOST declaration from your .env file.
I have an exception in one of my views. However, instead of telling me the name of the view so I can find it and fix it, laravel says it is in app/storage/views/110a3ecc0aa5ab7e6f7f50ef35a67a8b, which is meaningless.
How do I disable this view caching, so that laravel uses and refers to the actual files?
Out of the box? You can't. But you can extend the BladeCompiler class, overriding the method resposible for checking if the view has been expired:
class MyBladeCompiler extends BladeCompiler {
public function isExpired($path)
{
if ( ! \Config::get('view.cache'))
{
return true;
}
return parent::isExpired($path);
}
}
You'll need to replace the BladeCompiler instance in IoC container, with your own compiler:
$app = App::make('app'); // or just $app = app();
$app->bindShared('blade.compiler', function($app)
{
$cache = $app['path.storage'].'/views';
return new MyBladeCompiler($app['files'], $cache);
});
And then you just need to create that key in your app/config/view.php file
<?php
return [
'cache' => false,
'paths' => [base_path().'/resources/views'],
'pagination' => 'pagination::slider-3',
];
Or, like I do here:
return [
'cache' => in_array(App::environment(), ['production', 'staging']),
];
this worked for me... added this to the .env file
CACHE_EXPIRE=-1
In latest version of laravel (> v9.7.0), you can now add inside config/view.php:
'cache' => App::environment('local') ? false : true
Here is the PR: https://github.com/laravel/framework/pull/41859
Solution
open php.ini
opcache.revalidate_freq=0
opcache.fast_shutdown=0
change to this. restart apache.
check your .env file
Change CACHE_DRIVER=file to CACHE_DRIVER=array
If you have artisan, it's easy to clear the cache
php artisan view:clear
If you don't have or don't want artisan (can't think why you wouldn't want it, it's very useful), you can from the root of your project do
cd storage/framework/views/
rm *.php
Laravel Creates view cache file because it has been told to do that. In .env File you will come across cache_driver which has default property as file change it to array.
You can clear cache in this way, as well:
// Clear cache in laravel
Route::get('/clear-cache', function() {
Artisan::call('cache:clear');
// return what you want
return "Cache is cleared";
});
Here is the full answer
Go to vendor/illuminate/BladeCompiler.php
change these 2 lines
use Illuminate\View\Compilers\Compiler;
class BladeCompiler extends Compiler implements CompilerInterface
with the following:
use App\Support\CustomCompiler;
class BladeCompiler extends CustomCompiler implements CompilerInterface
in your app/support folder (or whatever structure you are using)
create the following class
namespace App\Support;
use Illuminate\View\Compilers\Compiler;
class CustomCompiler extends Compiler {
public function isExpired($path) {
if ( !\config('blade.use_cache'))
return true;
return parent::isExpired($path);
}
}
your blade config file will look like this
return [
'use_cache' => false,
'cache' => storage_path('cache'),
'views' => resources_path('views')
];
auto dump and run....
If you are using MAMP, disable OPCache under Preferences, General, PHP-Cahce. just select off. thank me later.
Although some would call this sketchy, this was the quickest and most minimal way to do this on a small application I was working on
On the controller(s) that my routes pointed to:
public function __construct()
{
exec('php /full/path/to/artisan view:clear');
}
A bit late to the party, however.
I had the same issue: the browser not reflecting changes to the php code.
Simple solution for me was:
set the clock on the server to the same time as the dev computer !
sudo date +%T -s "11:14:00"
In development environment, I just add and modify the next:
bootstrap/start.php
$env = $app->detectEnvironment(function(){return 'testing';});
app/config/testing/cache.php add in array
'cache' => false,
app/config/view.php add in array
'cache' => false,
I am using Symfony2 and when i try to generate the schema ($ php app/console doctrine:generate:schema) i got an error..
[Doctrine\ORM\Mapping\MappingException]
No mapping file found named 'xxx.UserBundle.Entity.User.php' for class 'xxx\UserBundle\Entity\User'.
I only have 2 Bundles in the proyect:
UserBundle
FileBundle
I connect the FileBundle with the UserBundle with this code:
/**
* #ORM\ManyToOne(targetEntity="xxx\UserBundle\Entity\User")
**/
protected $user;
The headers of the files are something like this:
namespace xx\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
**/
class User
{ ###...###}
FileBundle is very similar..
Thanks!
You are mixing Doctrine mapping formats, you have annotations and at least one XML file in Resources/config/doctrine
From the symfony docs:
"A bundle can accept only one metadata definition format. For example,
it's not possible to mix YAML metadata definitions with annotated PHP
entity class definitions."
So the solution is:
You cannot mix different Doctrine mapping formats in a given bundle. So either use annotations for all entities or use XML for all.
It is somehow strange to me that you're using PHPDriver for ORM and not AnnotationDriver, since your database info in classes is in annotations.
Anyhow, if php app/console doctrine:mapping:info command gives you only 1 entity that means that your other bundle containing User class is not loaded in app/AppKernel.php file. Load your UserBundle by adding line
new xxx\UserBundle\xxxUserBundle(),
to the $bundles array in registerBundles() function. After that, this function should look something like this:
public function registerBundles()
{
$bundles = array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new Symfony\Bundle\MonologBundle\MonologBundle(),
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
new Symfony\Bundle\DoctrineBundle\DoctrineBundle(),
new Symfony\Bundle\AsseticBundle\AsseticBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(),
new xx\FileBundle\xxFileBundle(),
new xxx\UserBundle\xxxUserBundle(),
);
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
$bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
$bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
}
return $bundles;
}
Of course, change 'xx' and 'xxx' with your real names.
Hope this helps.
I agree with #ilanco 100%. In addition, the solution is to REMOVE any .xml file in folder, for example:
C:\xampp\htdocs\localxyz\src\AppBundle/Resources/config/doctrine/Comment.orm.xml
these xml files created when you run such command:
C:\xampp\htdocs\localxyz>php app/console doctrine:mapping:import --force AppBundle xml
Dung.
Good day,
Although I know that this has been posted years ago, but will just like to post my answer here, perhaps it could help someone :)
just simply clear the cache, it works for me though
php bin/console cache:clear
Thanks
I can't think of a reason why this is happening but I will take a leap here.
Try editing your config.yml. doctrine section should look like this:
doctrine:
dbal:
default_connection: my_connection_name
connections:
fmefb:
host: %database_host%
dbname: %database_name%
user: %database_user%
password: %database_password%
driver: %database_driver%
port: %database_port%
charset: UTF8
orm:
default_entity_manager: my_entity_manager
entity_managers:
my_entity_manager:
connection: my_connection_name
mappings:
CompanyNameSiteBundle: ~
CompanyNameAdminBundle: ~
CompanyNameSomethingElse: ~
Notice the mappings list on the bottom. All bundles which are "linked" are included here.
Hope this helps....