How to write Monolog logs into a file AND remote database - mongodb

I have a Symfony2 -project and it writes the logs into different files beautifully, but I would like it to write the logs into a remote database(mongodb) as well. I would like to keep the actual log files in the servers as a backup in case something goes wrong with the database connection.
Question 1:
Is it even possible to save the same logs into two different places at the same time?
Question 2:
How do I save the logs into the mongodb? I don't necessarily need specific mongodb-instructions, but some guidelines on how to write into a remote db with monologger. The mongodb-specific instructions are also welcome if available. ;)
Question 3(OPTIONAL):
Can I get a full error stack into the logs somehow? Where could one find a full list of what data the Monolog can actually write and how to write?

There was a very good Blogpost sometime back for logging to a mysql database with monolog and doctrine. I can't find it anymore so i will just add the neccessary Files here and you can adjust it.
The whole logic is done in the DatabaseHandler so you can just change from
mysql inserts to a handling for your mongodb.
This code is not mine if anyone knows the original post please comment.
BacktraceLoggerListener.php
namespace UtilsBundle\EventListener;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
class BacktraceLoggerListener{
private $_logger;
public function __construct(LoggerInterface $logger = null)
{
$this->_logger = $logger;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
$this->_logger->addError($event->getException());
}
}
DatabaseHandler.php
namespace UtilsBundle\Logger;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Logger;
/**
* Stores to database
*
*/
class DatabaseHandler extends AbstractProcessingHandler{
protected $_container;
/**
* #param string $stream
* #param integer $level The minimum logging level at which this handler will be triggered
* #param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct($level = Logger::DEBUG, $bubble = true)
{
parent::__construct($level, $bubble);
}
/**
*
* #param type $container
*/
public function setContainer($container)
{
$this->_container = $container;
}
/**
* {#inheritdoc}
*/
protected function write(array $record)
{
// Ensure the doctrine channel is ignored (unless its greater than a warning error), otherwise you will create an infinite loop, as doctrine like to log.. a lot..
if( 'doctrine' == $record['channel'] ) {
if( (int)$record['level'] >= Logger::WARNING ) {
error_log($record['message']);
}
return;
}
// Only log errors greater than a warning
// TODO - you could ideally add this into configuration variable
if( (int)$record['level'] >= Logger::NOTICE ) {
try
{
// Logs are inserted as separate SQL statements, separate to the current transactions that may exist within the entity manager.
$em = $this->_container->get('doctrine')->getManager();
$conn = $em->getConnection();
$created = date('Y-m-d H:i:s');
$serverData = ""; //$record['extra']['server_data'];
$referer = "";
if (isset($_SERVER['HTTP_REFERER'])){
$referer= $_SERVER['HTTP_REFERER'];
}
$stmt = $em->getConnection()->prepare('INSERT INTO system_log(log, level, server_data, modified, created)
VALUES(' . $conn->quote($record['message']) . ', \'' . $record['level'] . '\', ' . $conn->quote($referer) . ', \'' . $created . '\', \'' . $created . '\');');
$stmt->execute();
} catch( \Exception $e ) {
// Fallback to just writing to php error logs if something really bad happens
error_log($record['message']);
error_log($e->getMessage());
}
}
}
}
We used xml here but this can be done in
services.yml too
services.xml
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="utils.database.logger" class="UtilsBundle\Logger\DatabaseHandler">
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
</service>
<service id="utils.backtrace.logger.listener" class="UtilsBundle\EventListener\BacktraceLoggerListener">
<argument type="service" id="logger" />
<tag name="monolog.logger" channel="backtrace" />
<tag name="kernel.event_listener" event="kernel.exception" method="onKernelException" />
</service>
</services>
And lastly add the handler to your monolog config in
config_**.yml so here for production for example
config_prod.yml
monolog:
handlers:
main:
type: rotating_file
action_level: error
max_files: 10
handler: nested
nested:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
console:
type: console
database:
type: service
level: notice
id: utils.database.logger
channels: ["!translation"]
Hope that helps

Hope I can some things up for you:
Question 1: Yes its possible. E.G. you can do smt. like:
$this->logger->pushHandler(new StreamHandler('/path/to/logs/123_info.log', Logger::INFO));
$this->logger->pushHandler(new StreamHandler('/path/to/logs/456_warning.log', Logger::INFO));
So if $this->logger->addInfo("testinfo"); this is getting logged in both streams.
Question 2: There is a MongoDBHandler as according to the StreamHandler. You should be able do configure it and pass it along to the pushHandler method or if you want to have it in your services look at MongoDBConfiguration.
Question 3:
This should help: Configure Monolog
Hope that helps.

Related

Override magento 2 block ../module_sales/block/adminhtml/totals.php

I am trying to override magento block but everytime main block from vendor is executed. No errors are shown.
Magento block:
vendor/magento/module_sales/block/adminhtml/totals.php
Created block in custom module:
[vendor]/[module]/block/adminhtml/totals.php
Modified di.xml file in:
[vendor]/[module]/etc/di.xml
Preference in di.xml file:
...
<preference for="Magento\Sales\Block\Adminhtml\Totals"
type="Iways\Sales\Block\Adminhtml\Totals" />
...
Content of block in custom module:
namespace Iways\Sales\Block\Adminhtml;
use Magento\Framework\DataObject;
use Magento\Sales\Block\Adminhtml\Totals as DefaultTotals;
class Totals extends DefaultTotals
{
...
I have tried to check if file is executed with xdebug but it isnt.
If you override a block, you also want to add a sequence in your module.xml. You have to make sure the module of the block you want to override is being loaded before your module is being loaded. See Component load order. Add the module Magento_Sales to your sequence.
If that doesn't work:
Are you sure your module is registered by Magento, can you find your module in `app/etc/config.php'?
Are you sure there's no other module which overrides that block already?
The block that I was trying to extend has already been extended by another block in:
module_sales/block/adminhtml/order/totals.php
So in general, all I needed to do is to extend that block mentioned above.
Posting this solution for Magento v-2.3.5
Override this class \Magento\Sales\Block\Adminhtml\Order\Totals
app/code/Taktheer/ExtendCoupanTotals/etc/di.xml
<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Sales\Block\Adminhtml\Order\Totals" type="Taktheer\ExtendCoupanTotals\Rewrite\Magento\Sales\Block\Adminhtml\Order\Totals"/>
</config>
app/code/Taktheer/ExtendCoupanTotals/Rewrite/Magento/Sales/Block/Adminhtml/Order/Totals.php
<?php
/**
* Copyright © Unyscape Infocom Pvt. Ltd. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Taktheer\ExtendCoupanTotals\Rewrite\Magento\Sales\Block\Adminhtml\Order;
class Totals extends \Magento\Sales\Block\Adminhtml\Order\Totals
{
/**
* Initialize order totals array
*
* #return $this
*/
protected function _initTotals()
{
parent::_initTotals();
$order = $this->getSource();
if ($order->getCouponCode()) {
$discountLabel = __('Discount (%1)', $order->getCouponCode());
} else {
$discountLabel = __('Discount');
}
$this->_totals['discount'] = new \Magento\Framework\DataObject(
[
'code' => 'discount',
'value' => $order->getDiscountAmount(),
'base_value' => $order->getBaseDiscountAmount(),
'label' => $discountLabel,
]
);
return $this;
}
}
======================== Happy Coding =============================

doctrine migrations bundle and postgres schema: diff does not work properly

I'm using doctrine in my Symfony project, by connecting to an already existent postgres database.
The DB has several schemas, but the symfony app will use nothing but its own schema. The first Entity class I created is the following one:
namespace Belka\TestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="app_auth.User", schema="app_auth")
*/
class User {
/**
* #ORM\Column(type="string")
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
*/
private $username;
/**
* #ORM\Column(type="string")
*/
private $email;
/**
* #ORM\Column(type="string")
*/
private $password;
}
as you can see, the Entity is specifying its own schema app_auth.
Next, I tried to use the migrations bundle. So, I installed and configured it, in order not to consider anything but my schema:
Extract of config.yml:
doctrine:
dbal:
driver: "%database_driver%"
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
schema_filter: ~^app_auth\..*~
Extract of config_dev.yml:
doctrine_migrations:
dir_name: "%kernel.root_dir%/../.container/update/DoctrineMigrations"
namespace: Application\Migrations
table_name: "app_auth.migration_versions"
name: Application Migrations
And I run the diff:
php app/console doctrine:migrations:diff
Unfortunately, the migration class generated is the following one:
namespace Application\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20160422171409 extends AbstractMigration
{
/**
* #param Schema $schema
*/
public function up(Schema $schema)
{
// this up() migration is auto-generated, please modify it to your needs
}
/**
* #param Schema $schema
*/
public function down(Schema $schema)
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
$this->addSql('CREATE SCHEMA app_auth');
}
}
what's wrong with my configuration then?
Dirty but working solution:
Tested with doctrine/migrations: 3.0.1
We can patch out the schema removing functionality of the DiffGenerator. I personally used this https://github.com/cweagans/composer-patches
Install the package, and then in composer.json "extra" section add:
"patches": {
"doctrine/migrations": {
"Change doctrine behavior to correctly pass PostgreSQL schema to schema_filter while diffing": "patches/doctrine-diff-generator.patch"
}
}
The patch file in patches/doctrine-diff-generator.patch:
--- lib/Doctrine/Migrations/Generator/DiffGenerator.php 2020-06-21 10:55:42.000000000 +0200
+++ lib/Doctrine/Migrations/Generator/DiffGenerator.patched.php 2020-12-23 12:33:02.689405221 +0100
## -142,8 +142,6 ##
*/
private function resolveTableName(string $name) : string
{
- $pos = strpos($name, '.');
-
- return $pos === false ? $name : substr($name, $pos + 1);
+ return $name;
}
}
Of course you have to bare in mind, that updates to doctrine could break the patch. And you are changing this functionality globally for your doctrine, so beware of breaking changes.
Personally, for my use case, which is adding doctrine entities into legacy database, without breaking it, this works nicely, and saves me from adding every new table managed by doctrine into the schema_filter.
Explanation:
When we look into DiffGenerator implementation it actually decodes the table names with schema correctly, but only when collecting the current database schema. This happens in PostgreSqlSchemaManager.php:
/**
* {#inheritdoc}
*/
protected function _getPortableTableDefinition($table)
{
$schemas = $this->getExistingSchemaSearchPaths();
$firstSchema = array_shift($schemas);
if ($table['schema_name'] === $firstSchema) {
return $table['table_name'];
}
return $table['schema_name'] . '.' . $table['table_name'];
}
But then then when calculating the target schema, this information is lost here in DiffGenerator.php:
/**
* Resolve a table name from its fully qualified name. The `$name` argument
* comes from Doctrine\DBAL\Schema\Table#getName which can sometimes return
* a namespaced name with the form `{namespace}.{tableName}`. This extracts
* the table name from that.
*/
private function resolveTableName(string $name) : string
{
$pos = strpos($name, '.');
return $pos === false ? $name : substr($name, $pos + 1);
}
And only afterwards the return value of this function is passed into the schema_filter. Unfortunate, but i guess there was a reason for this :)

Magento2- How to copy custom data from quote to order and order item

I am building a custom module in Magento 2 that has a custom discount. I am trying to copy the discount from quote, quote item to order and order item.
In Magento 1, I declare the config.xml like this:
<fieldsets>
<sales_convert_quote_address>
<custom_discount_amount><to_order>*</to_order></custome_discount_amount>
<base_custom_discount_amount><to_order>*</to_order></base_custome_discount_amount>
</sales_convert_quote_address>
<sales_convert_quote_item>
<custome_discount_amount><to_order_item>*</to_order_item></custome_discount_amount>
<base_custom_discount_amount><to_order_item>*</to_order_item></base_custom_discount_amount>
</sales_convert_quote_item>
</fieldsets>
and my custom discount amount was copied to tables: sales_flat_order and sales_flat_order_item as expected.
In Magento 2, I created a file named fieldset.xml with this code:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Object/etc/fieldset.xsd">
<scope id="global">
<fieldset id="sales_convert_quote_item">
<field name="custom_discount_amount">
<aspect name="to_order_item" />
</field>
<field name="base_custom_discount_amount">
<aspect name="to_order_item" />
</field>
</fieldset>
<fieldset id="sales_convert_quote_address">
<field name="custom_discount_amount">
<aspect name="to_order" />
</field>
<field name="base_custom_discount_amount">
<aspect name="to_order" />
</field>
</fieldset>
</scope>
</config>
but there is no success.
What else do I need to do in Magento 2 to make it work? Can you guys please help me?
In Magento 2 without using fieldset you can also copy custom data from quote item to order item by using plugin.
create di.xml in your module etc folder.
app/code/Vender/Yourmodule/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
<type name="Magento\Quote\Model\Quote\Item\ToOrderItem">
<plugin name="cedapi_quote_to_order_item" type="Vender\Yourmodule\Model\Plugin\Quote\QuoteToOrderItem"/>
</type>
</config>
Create a class to your module and define a function. app/code/Vender/Yourmodule/Model/Plugin/Quote
Create QuoteToOrderItem.php file
<?php
namespace Vender\Yourmodule\Model\Plugin\Quote;
use Closure;
class QuoteToOrderItem
{
/**
* #param \Magento\Quote\Model\Quote\Item\ToOrderItem $subject
* #param callable $proceed
* #param \Magento\Quote\Model\Quote\Item\AbstractItem $item
* #param array $additional
* #return \Magento\Sales\Model\Order\Item
* #SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function aroundConvert(
\Magento\Quote\Model\Quote\Item\ToOrderItem $subject,
Closure $proceed,
\Magento\Quote\Model\Quote\Item\AbstractItem $item,
$additional = []
) {
/** #var $orderItem \Magento\Sales\Model\Order\Item */
$orderItem = $proceed($item, $additional);//result of function 'convert' in class 'Magento\Quote\Model\Quote\Item\ToOrderItem'
$orderItem->setCustomDesign($item->getCustomDesign());//set your required
return $orderItem;// return an object '$orderItem' which will replace result of function 'convert' in class 'Magento\Quote\Model\Quote\Item\ToOrderItem'
}
}
After spend some time and research issue, i stucked here:
Magento\Quote\Model\QuoteManagement.php
line 446
public function mergeDataObjects(
$interfaceName,
$firstDataObject,
$secondDataObject
) {
if (!$firstDataObject instanceof $interfaceName || !$secondDataObject instanceof $interfaceName) {
throw new \LogicException('Wrong prototype object given. It can only be of "' . $interfaceName . '" type.');
}
$secondObjectArray = $this->objectProcessor->buildOutputDataArray($secondDataObject, $interfaceName);
$this->_setDataValues($firstDataObject, $secondObjectArray, $interfaceName);
return $this;
}
Which ignores converted attributes because logic of merging based on presence getters and setters of target model\interface. So, if you converting attributes which haven't setters and getters in target model they will be ignored here:
Magento\Framework\Reflection\DataObjectProcessor.php line 75
public function buildOutputDataArray($dataObject, $dataObjectType)
{
$methods = $this->methodsMapProcessor->getMethodsMap($dataObjectType);
$outputData = [];
/** #var MethodReflection $method */
foreach (array_keys($methods) as $methodName) {
if (!$this->methodsMapProcessor->isMethodValidForDataField($dataObjectType, $methodName)) {
continue;
}
$value = $dataObject->{$methodName}();
$isMethodReturnValueRequired = $this->methodsMapProcessor->isMethodReturnValueRequired(
$dataObjectType,
$methodName
);
Maybe you might to use observer or plugin to avoid this problem. (issue encountered in 2.0.6 Magento version)
For anybody looking at this in the future, the fact that the fieldset XML doesn't work has been acknowledged by Magento as a bug. There is a core patch available in the ticket (not reproduced here since it may need to be tweaked with new Magento versions).
https://github.com/magento/magento2/issues/5823

How to render Twig template from database in symfony2

I'm working on application written in symfony2 and I want to send email after some action/event... the problem is, that the users can define something like "email templates" which are stores in db like simple string, for example: "This is some email from {{ user }}" and I need to render body for that email which should use that template...
In symfony documentation from this link: https://symfony.com/doc/2.0/cookbook/email/email.html#sending-emails the method for render view is $this->renderView and it expects the path to file such as "bundle:controller:file.html.twig", but my template is simple string from database...
How can I render it?
Twig_Loader_String is deprecated and was always designed for internal use anyway. The usage of this loader is strongly discouraged.
From the API doc:
This loader should NEVER be used. It only exists for Twig internal
purposes. When using this loader with a cache mechanism, you should
know that a new cache key is generated each time a template content
"changes" (the cache key being the source code of the template). If
you don't want to see your cache grows out of control, you need to
take care of clearing the old cache file by yourself.
Also check out this issue: https://github.com/symfony/symfony/issues/10865
The best way I know to load a template from a String source are:
From a controller:
$template = $this->get('twig')->createTemplate('Hello {{ name }}');
$template->render(array('name'=>'World'));
as described here: http://twig.sensiolabs.org/doc/recipes.html#loading-a-template-from-a-string
From a twig template:
{{ include(template_from_string("Hello {{ name }}", {'name' : 'Peter'})) }}
as described here: http://twig.sensiolabs.org/doc/functions/template_from_string.html
Note, that the 'template_from_string' - function is not available by default and needs to be loaded. In symfony you would do this by adding a new service:
# services.yml
services:
appbundle.twig.extension.string:
class: Twig_Extension_StringLoader
tags:
- { name: 'twig.extension' }
This should work. Replace "Hello {{ name }}" with your template text, and fill the array that is passed into the render function with any variables that you need.
$env = new \Twig_Environment(new \Twig_Loader_String());
echo $env->render(
"Hello {{ name }}",
array("name" => "World")
);
Here's a solution that works with Symfony 4 (and possibly older versions as well, although I haven't tested it) and allows you to work with templates stored in the database the same way you would work with templates in the filesystem.
This answer assumes you're using Doctrine, but is relatively easy to adapt if you're using another database library.
Create the Template entity
This is an example class that uses annotations, but you can use whatever configuration method you're already using.
src/Entity/Template.php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="templates")
* #ORM\Entity
*/
class Template
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string", nullable=false)
*/
private $filename;
/**
* #var string
*
* #ORM\Column(type="text", nullable=false)
*/
private $source;
/**
* #var \DateTime
*
* #ORM\Column(type="datetime", nullable=false)
*/
private $last_updated;
}
The bare minimum fields are filename and source, but it's a very good idea to include last_updated or you'll lose the benefits of caching.
Create a DatabaseLoader class
src/Twig/Loader/DatabaseLoader.php
<?php
namespace App\Twig\Loader;
use App\Entity\Template;
use Doctrine\ORM\EntityManagerInterface;
use Twig_Error_Loader;
use Twig_LoaderInterface;
use Twig_Source;
class DatabaseLoader implements Twig_LoaderInterface
{
protected $repo;
public function __construct(EntityManagerInterface $em)
{
$this->repo = $em->getRepository(Template::class);
}
public function getSourceContext($name)
{
if (false === $template = $this->getTemplate($name)) {
throw new Twig_Error_Loader(sprintf('Template "%s" does not exist.', $name));
}
return new Twig_Source($template->getSource(), $name);
}
public function exists($name)
{
return (bool)$this->getTemplate($name);
}
public function getCacheKey($name)
{
return $name;
}
public function isFresh($name, $time)
{
if (false === $template = $this->getTemplate($name)) {
return false;
}
return $template->getLastUpdated()->getTimestamp() <= $time;
}
/**
* #param $name
* #return Template|null
*/
protected function getTemplate($name)
{
return $this->repo->findOneBy(['filename' => $name]);
}
}
The class is relatively simple. getTemplate looks up the template filename from the database, and the rest of the methods use getTemplate to implement the interface that Twig needs.
Add the DatabaseLoader to your service config
config/services.yaml
services:
App\Twig\Loader\DatabaseLoader:
tags:
- { name: twig.loader }
Now you can use your database templates in the same way as filesystem templates.
Rendering from a controller:
return $this->render('home.html.twig');
Including from another Twig template (which can be in the database or filesystem):
{{ include('welcome.html.twig') }}
Rendering to a string (where $twig is an instance of Twig\Environment)
$html = $twig->render('email.html.twig')
In each of these cases, Twig will check the database first. If getTemplate in your DatabaseLoader returns null, Twig will then check the filesystem. If the template isn't available in the database or the filesystem, Twig will throw a Twig_Error_Loader.
Clone the native twig service and replace the filesystem loader with the native twig string loader:
<service id="my.twigstring" class="%twig.class%">
<argument type="service" id="my.twigstring.loader" />
<argument>%twig.options%</argument>
</service>
<service id="my.twigstring.loader" class="Twig_Loader_String"></service>
Usage example from within a controller:
$this->get('my.twigstring')->render('Hello {{ name }}', array('name' => 'Fabien'));
The best way to do it is to use template_from_string twig function.
{{ include(template_from_string("Hello {{ name }}")) }}
{{ include(template_from_string(page.template)) }}
See documentation of template_from_string
See why it is not a good idea to use Twig_Loader_Chain or Twig_Loader_String for that purpose on this github issue by stof.
As of Twig 1.10, the Twig Engine doesn't support rendering strings. But there is a bundle available which adds this behavior called TwigstringBundle.
It adds the $this->get('twigstring') service wich you can use to render your strings.
(As September '19, the current version of Twig is 2.X, and version 3 is around the corner; so this is only applies to very old versions of Twig).
This work for me:
$loader = new \Twig\Loader\ArrayLoader([
'Temp_File.html' => 'Hello {{ name }}!',
]);
$twig = new \Twig\Environment($loader);
echo $twig->render('Temp_File.html', ['name' => 'Fabien']);
https://twig.symfony.com/doc/2.x/api.html
FYI, This feature was suggested to be added in the core of Twig as of 1.11.0, but will be needed to be activated by the developper.
With Symfony 2.2 you can use the Twig_Chain_Loader
How to register another (custom) Twig loader in Symfony2 environment?
I recently had to implement a CMS used by multiple parties where each party could completely customize their templates. To achieve this I implemented a custom Twig Loader.
The most difficult part was coming up with a naming convention for the templates guaranteed not to overlap with any existing templates, for example <organisation_slug>!AppBundle:template.html.twig.
In case the template was not customised, the template AppBundle:template.html.twig would have to be loaded as fallback template.
However, this is not possible with the Chain Loader (AFAIK) because there the template name cannot be modified. Therefore I had to inject the default loader (i.e. the loader chain) into my loader and use it to load the fallback template.
Another solution would be to pass the request stack or the session to the template loader, making it possible to automatically detect the organisation, but this is difficult because the security component depends on the templating subsystem, causing circular dependency issues.
$message = \Swift_Message::newInstance()
->setSubject('Hello Email')
->setFrom('send#example.com')
->setTo('recipient#example.com')
->setBody('hai its a sample mail')
;
$this->get('mailer')->send($message);

Unit Testing with PHPUnit & Doctrine2: Autoloading Failed

I am getting this error
D:\Projects\Tickle\tests>phpunit
PHPUnit 3.5.5 by Sebastian Bergmann.
..........ok1
Fatal error: Doctrine\Common\ClassLoader::loadClass(): Failed opening required 'D:\Projects\Tickle\Application\Models\Ap
plication\Models\User.php' (include_path='D:\Projects\Tickle\application/../library;D:\Projects\Tickle\application;D:\Pr
ojects\Tickle\library;D:\ResourceLibrary\Frameworks\PHPFrameworks;C:\Program Files (x86)\PHP\pear;.;c:\php\includes') in
D:\ResourceLibrary\Frameworks\PHPFrameworks\Doctrine\Common\ClassLoader.php on line 148
Notice its repetition "D:\Projects\Tickle\Application\Models\Application\Models\User.php" of Application\Models. I narrowed it down to 1 function of my unit tests with the help of some echo statements
protected function isAllowed($userId, $taskId, $privilege) {
$user = $this->em->find('Application\Models\User', $userId);
echo 'ok1'; // this gets printed
$task = $this->em->find('Application\Models\Task', $taskId); // this seem to be the cause of the problem
echo 'ok2'; // this doesn't get printed
return $this->acl->isAllowed($user, $task, $privilege);
}
The full class http://pastebin.com/rJungFvP
So I tried looking of something in the Tasks class that uses User ... the only thing that might use the User class are
/**
* #ManyToOne(targetEntity="User", inversedBy="ownedTasks")
*/
protected $owner;
/**
* #ManyToOne(targetEntity="User", inversedBy="assignedTasks")
*/
protected $assigned;
apart from that, I don't see use of any Users. full class http://pastebin.com/wDPXUPSV.
What did I do wrong?
The problem was actually found in the TodoList class ... I had something like
public function setProject(Application\Models\Project $project) {
...
}
when I should be using something like
public function setProject(Project $project) {
$this->project = $project;
}
instead