Behat/Mink - cant find element by xpath with goutteDriver - selenium-webdriver

i am using Behat 3.0 and Mink 1.6.
Those codes work with Selenium2 and Zombie, but not with Goutte:
$this->assertSession()->elementTextContains('xpath', "//div[#id='pagecontent-shop']/form/table/tbody/tr[12]/td[2]", $arg1);
$page = $this->getSession()->getPage();
$element = $page->find('xpath', "//div[#id='pagecontent-shop']/form/table/tbody/tr[12]/td[2]",)->getText();
Does anyone knows what is happening?

Did you try findById()?
e.g.
/**
* #When /^I click an element with ID "([^"]*)"$/
*
* #param $id
* #throws \Exception
*/
public function iClickAnElementWithId($id)
{
$element = $this->getSession()->getPage()->findById($id);
if (null === $element) {
throw new \Exception(sprintf('Could not evaluate element with ID: "%s"', $id));
}
$element->click();
}
e.g.
/**
* Click on the element with the provided css id
*
* #Then /^I click on the element with id "([^"]*)"$/
*
* #param $elementId ID attribute of an element
* #throws \InvalidArgumentException
*/
public function clickElementWithGivenId($elementId)
{
$session = $this->getSession();
$page = $session->getPage();
$element = $page->find('css', '#' . $elementId);
if (null === $element) {
throw new \InvalidArgumentException(sprintf('Could not evaluate CSS: "%s"', $elementId));
}
$element->click();
}
EDIT:
Example below goes thru input elements in a table and finds the ones with specific name. You need to modify it a bit though.
/**
* #Given /^the table contains "([^"]*)"$/
*/
public function theTableContains($arg1)
{
$session = $this->getSession();
$element = $session->getPage()->findAll('css', 'input');
if (null === $element) {
throw new \Exception(sprintf('Could not evaluate find select table'));
}
$options = explode(',', $arg1);
$match = "element_name";
$found = 0;
foreach ($element as $inputs) {
if ($inputs->hasAttribute('name')) {
if (preg_match('/^'.$match.'(.*)/', $inputs->getAttribute('name')) !== false) {
if (in_array($inputs->getValue(), $options)) {
$found++;
}
}
}
}
if (intval($found) != intval(count($options))) {
throw new \Exception(sprintf('I only found %i element in the table', $found));
}
}

Related

Duplicate rows in the database when refreshing the page laravel

there is a page which is updated every 5-7 seconds, and on it records from the base are updated, but moments these records are duplicated, Do not tell me why this bug can be?
is a ActiveCaller model
namespace App;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
class ActiveCaller extends Model
{
protected $fillable = ['queue', 'employee_id', 'station', 'state', 'duration',
'client_id', 'last_call'];
public function Employee()
{
return $this->belongsTo(Employee::class);
}
/**
* Convert duration attribute to acceptable format
*
* #param $value
* #return string
*/
public function getDurationAttribute($value)
{
if (empty($value))
return $value;
return $this->sec2hms($value);
}
public function getStateAttribute($value)
{
if (!empty($value))
return trim($value);
return null;
}
/**
* Convert last call attribute to acceptable format
*
* #param $value
* #return string
*/
public function getLastCallAttribute($value)
{
$data = explode("\n", $value);
$result = "";
$i = 0;
$len = count($data) - 1;
foreach ($data as $item) {
$item = str_replace("\r", "", $item);
$delimiter = "</br>";
if ($i == $len)
$delimiter = "";
if (empty($item) || (trim($item) == "No calls yet")) {
$result .= "No calls yet$delimiter";
} else {
$result .= $this->sec2hms($item) . " min. ago $delimiter";
}
$i++;
}
return $result;
}
public function getStationAttribute($value)
{
return str_replace("\r\n", "</br>", $value);
}
private function sec2hms($sec, $padHours = FALSE)
{
$timeStart = Carbon::now();
$timeEnd = Carbon::now()->addSeconds(intval($sec));
return $timeStart->diff($timeEnd)->format('%H:%I:%S');
}
}
is a AmiApiController
class AmiApiController extends Controller
{
public function fetchDashboardData()
{
$this->updateQueueState();
$activeCallers = ActiveCaller::with('Employee')
->where('old', true)
->orderBy('queue')
->orderBy('employee_id')
->orderBy('station', 'asc')
->get();
$waitingList = WaitingList::where('old', false)->get();
$waitingList = $waitingList->unique('client_id');
$charts = Chart::all()->toArray();
$chartFormatData = [
'Total' => [],
'Callers' => [],
'Queues' => [],
];
foreach ($charts as $key => $chart) {
$charts[$key]['data'] = json_decode($chart['data'], 1);
$chartFormatData[$chart['name']]['total'] = 0;
foreach ($charts[$key]['data']['statistic'] as $datum) {
// if ($datum[0] === 'Effort')
// continue;
$chartFormatData[$chart['name']]['label'][] = $datum[0];
$chartFormatData[$chart['name']]['data'][] = $datum[1];
$chartFormatData[$chart['name']]['name'] = $chart['name'];
}
$chartFormatData[$chart['name']]['total'] = array_sum($chartFormatData[$chart['name']]['data']);
// $chartFormatData[$chart['name']]['label'] = array_reverse($chartFormatData[$chart['name']]['label']);
}
return response()->json([
'activeCallers' => $activeCallers,
'charts' => $chartFormatData,
'waitingList' => $waitingList
], 200);
}
this is where we begin to check if we can update the database at this time
/**
* Check whether the database can be updated at this time
*
* - Returns True if no updates are currently being made to the database
* and the latest update was less than 5 seconds later
* -
Returns True if the update already occurs for more than 15 seconds
*
* - Returns False if an update is already in the database
* -
Returns False if the last update was within the last 5 seconds
*
* If the parameter in $ json is passed true (by default)
* the method returns the answer in JSON format
*
* If the parameter is passed false to $ json
* method returns a php-shne Boolean value
*
* #param bool $json
* #return bool|\Illuminate\Http\JsonResponse
*/
public function canWeUpdate($json = true)
{
$result = ['return' => null, 'msg' => null];
$isUpdating = Configuration::where('key', 'is_ami_data_updating')->first();
if (is_null($isUpdating)) {
Configuration::create(['key' => 'is_ami_data_updating', 'value' => 0]);
}
if ($isUpdating->value == true) {
// if an update is currently in progress
$checkingDate = Carbon::now()->addSeconds(-10);
if ($isUpdating->updated_at < $checkingDate) {
// if the update is longer than 15 seconds, we will cancel this update
$isUpdating->update(['value' => false]);
$result['return'] = true;
$result['msg'] = "Old update in database";
} else {
// if the update is less than 15 seconds, we cannot update again
$result['return'] = false;
$result['msg'] = "CURRENTLY UPDATE";
}
} else if ($isUpdating->updated_at > Carbon::now()->addSeconds(-3)) {
// if the last update was less than 5 seconds ago, we cannot update
$result['return'] = false;
$result['msg'] = "TOO EARLY";
} else {
//if the last update was more than 5 seconds ago, we allow the update
$result['return'] = true;
$result['msg'] = "OK";
}
if ($json)
return $this->simpleResponse($result['return'], $result['msg']);
return $result['return'];
}
is a method fot check if new data is in the database
/**
* A method to check if new data is in the database
*
* Returns True if validation time is less than database update time
* Returns False if validation time is longer than database update time
* Returns False if there is no data in the database
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\JsonResponse
*/
public function checkForNewData(Request $request)
{
$date = new Carbon($request->last_call);
$lastRecord = ActiveCaller::latest()->first();
if (is_null($lastRecord))
return $this->simpleResponse(false);
if ($date < $lastRecord->updated_at) {
return $this->simpleResponse(true);
} else
return $this->simpleResponse(false);
}
/**
* Method for loading table data
*
* Agents - information about active numbers in the PBX in all queues
* Waiting - information about numbers in standby mode
*
* #return \Illuminate\Http\JsonResponse
*/
public function renderAgentTable()
{
$agents = ActiveCaller::with('Employee')
->where('old', true)
->orderBy('queue')
->orderBy('station', 'asc')
->get();
$waitingList = WaitingList::all();
$agentsTable = View::make('dashboard.render.agent-table', ['agents' => $agents]);
$waitingTable = View::make('dashboard.render.waiting-list-table', ['waitingList' => $waitingList]);
$result =
[
'agents' => $agentsTable->render(),
'waiting' => $waitingTable->render(),
];
$result = array_merge($result, $this->renderDashboardChart(new Request()));
return response()->json($result);
}
/**
* Method for updating data from AMI
*
* updating data for ActiveCaller
* updating data for WaitingList
*/
public function updateQueueState()
{
if (!$this->canWeUpdate(false)) {
// dd("We can't update (large check)");
return;
}
$lastUpdateTime = ActiveCaller::latest()->first();
if ($lastUpdateTime != null)
if ($lastUpdateTime->created_at > Carbon::now()->addSeconds(-3)) {
// dd("We can't update (small check)");
return;
}
// we notice the launch of the update in the database
$isAmiDataUpdating = Configuration::where('key', 'is_ami_data_updating')->first();
$isAmiDataUpdating->update(['value' => true]);
$this->ClearOldRecords();
$queues = Queue::where('on_dashboard', '=', '1')->get()->toArray();
// we go through all the queues that are available in AMI
foreach ($queues as $queue) {
$command = new AMIQueueMemberState($queue['name']);
// we get a list of numbers and a waiting list of calls
$response = $command->handle();
$agents = $response['agents'];
$callers = $response['callers'];
//convert the waiting list to PeerList
$peerList = PeerInfo::hydrate($agents);
$employees = new Collection();
foreach ($callers as $caller) {
$caller['queue'] = $queue['name'];
WaitingList::create($caller);
}
$peerList->each(function (PeerInfo $peer) use ($employees) {
$record = Record
::where('phone', 'like', '%' . $peer->name . '%')
->where('end_date', null)
->get()->first();
$data = null;
if ($record != null) {
// check if this user already has an entry in active_callers (not old)
$active = ActiveCaller
::where('employee_id', $record['employee_id'])
->where('old', false)
->get()->first();
// if so, add him another number and
// we move on to the next iteration
if ($active != null) {
if ($this->HandleSingleActive($active, $peer->name, $peer->last_call))
return;
}
$peer->station = $record['station_name'];
$peer->employee_id = $record['employee_id'];
$a = collect($peer->toArray());
$data = $a->except(['name', 'pause'])->toArray();
$data['station'] = "$peer->station | $peer->name";
} else {
$peer->station = "- | $peer->name";
$a = collect($peer->toArray());
$data = $a->except(['name', 'pause'])->toArray();
}
ActiveCaller::create($data);
});
}
$this->updateDashboardChart();
$isAmiDataUpdating->update(['value' => false]);
}

Bypass spool when using thirdparty database spooling

I'm using a thirdparty bundle (Dextervip Citrax\DatabaseSwiftmailerBundle) to spool my email in a database. However, I still want to be able to bypass the spooling for some specific actions. Before, when I didn't use the bundle and used just regular spooling, I did this the following way:
public function sendSeparateMessage($subject, $fromEmail, $toEmail, $toCc, $body, $bypassSpool = false){
$message = \Swift_Message::newInstance()
->setSubject($subject)
->setFrom($fromEmail)
->setTo($toEmail)
->setCc($toCc)
->setBody($body)
->setContentType("text/html");
// Send the message
$failedRecipients = [];
$numSent = 0;
foreach ($toEmail as $address => $name)
{
if (is_int($address)) {
$toEmail = $message->setTo($name);
} else {
$toEmail = $message->setTo([$address => $name]);
}
$numSent += $this->mailer->send($message, $failedRecipients);
}
if($bypassSpool) {
$spool = $this->mailer->getTransport()->getSpool();
$transport = \Swift_MailTransport::newInstance();
$spool->flushQueue($transport);
}
}
So whenever the last parameter of that function was set to true, the emails got sent out immediately and weren't stored for spooling.
For my situation now, I'd like to have the same thing, but of course I'm calling the wrong flushQueue function because I'm probably not accessing the right spooler. Does anybody know how to access the spooler from that bundle?
The service class from that bundle:
<?php
/**
* Created by PhpStorm.
* User: Rafael
* Date: 02/05/2015
* Time: 22:16
*/
namespace Citrax\Bundle\DatabaseSwiftMailerBundle\Spool;
use Citrax\Bundle\DatabaseSwiftMailerBundle\Entity\Email;
use Citrax\Bundle\DatabaseSwiftMailerBundle\Entity\EmailRepository;
use Swift_Mime_Message;
use Swift_Transport;
class DatabaseSpool extends \Swift_ConfigurableSpool {
/**
* #var EmailRepository
*/
private $repository;
private $parameters;
public function __construct(EmailRepository $repository, $parameters)
{
$this->repository = $repository;
$this->parameters = $parameters;
}
/**
* Starts this Spool mechanism.
*/
public function start()
{
// TODO: Implement start() method.
}
/**
* Stops this Spool mechanism.
*/
public function stop()
{
// TODO: Implement stop() method.
}
/**
* Tests if this Spool mechanism has started.
*
* #return bool
*/
public function isStarted()
{
return true;
}
/**
* Queues a message.
*
* #param Swift_Mime_Message $message The message to store
*
* #return bool Whether the operation has succeeded
*/
public function queueMessage(Swift_Mime_Message $message)
{
$email = new Email();
$email->setFromEmail(implode('; ', array_keys($message->getFrom())) );
if($message->getTo() !== null ){
$email->setToEmail(implode('; ', array_keys($message->getTo())) );
}
if($message->getCc() !== null ){
$email->setCcEmail(implode('; ', array_keys($message->getCc())) );
}
if($message->getBcc() !== null ){
$email->setBccEmail(implode('; ', array_keys($message->getBcc())) );
}
if($message->getReplyTo() !== null ){
$email->setReplyToEmail(implode('; ', array_keys($message->getReplyTo())) );
}
$email->setBody($message->getBody());
$email->setSubject($message->getSubject());
$email->setMessage($message);
$this->repository->addEmail($email);
}
/**
* Sends messages using the given transport instance.
*
* #param Swift_Transport $transport A transport instance
* #param string[] $failedRecipients An array of failures by-reference
*
* #return int The number of sent emails
*/
public function flushQueue(Swift_Transport $transport, &$failedRecipients = null)
{
if (!$transport->isStarted())
{
$transport->start();
}
$count = 0;
$emails = $this->repository->getEmailQueue($this->getMessageLimit());
foreach($emails as $email){
/*#var $message \Swift_Mime_Message */
$message = $email->getMessage();
try{
$count_= $transport->send($message, $failedRecipients);
if($count_ > 0){
$this->repository->markCompleteSending($email);
$count += $count_;
}else{
throw new \Swift_SwiftException('The email was not sent.');
}
}catch(\Swift_SwiftException $ex){
$this->repository->markFailedSending($email, $ex);
}
}
return $count;
}
}
and the services.yml file:
services:
repository.email:
class: Citrax\Bundle\DatabaseSwiftMailerBundle\Entity\EmailRepository
factory: ['#doctrine.orm.default_entity_manager',getRepository]
arguments: ['CitraxDatabaseSwiftMailerBundle:Email']
citrax.database.swift_mailer.spool:
class: Citrax\Bundle\DatabaseSwiftMailerBundle\Spool\DatabaseSpool
arguments: ['#repository.email', '%citrax_database_swift_mailer.params%']
swiftmailer.spool.db:
alias: citrax.database.swift_mailer.spool
swiftmailer.mailer.default.spool.db:
alias: citrax.database.swift_mailer.spool

Doctrine searching on Entity without any filters

I was wondering if there is a way to search in entity without applying any filters. For Example I would like to build a textfiled in my template where a ajax post method is calling to a controller with purpose searching the whole entity.
My code:
$user = $this->getDoctrine()
->getRepository('AppBundle:QCE_SUBD')
->find('%'.$SearchParam.'%')
->getQuery();
$DSUB = $user->getArrayResult();
dump($DSUB);
I;m not sure how the function should be written, so if some one is willing to help it will be highly appreciate :)
You should just create a function that return a JsonResponse with an array of your result.
// In your controller
/**
* #Route("/ajax_action")
*/
public function ajaxAction(Request $request)
{
// Get the posted parameter from your ajax call
$searchParam = $request->get('searchParam');
// Request your entity
$user = $this->getDoctrine()
->getRepository('AppBundle:QCE_SUBD')
->createQueryBuilder('q')
->where('q.username LIKE :searchParam')
->orWhere('q.otherColumn LIKE :searchParam')
->setParameter('searchParam', '%'.$searchParam.'%')
->getQuery();
// Check if it's an ajax call
if ($request->isXMLHttpRequest()) {
return new JsonResponse($user->getArrayResult();
}
// Return an error
throw new \Exception('Wrong call!');
}
For the search part you need to implement a full text search, here is a tutorial on how to implement it :
http://ourcodeworld.com/articles/read/90/how-to-implement-fulltext-search-mysql-with-doctrine-and-symfony-3
P.S : You should be sure of what you need in your query. If you want it to be scalable, you should take a look at better search engine method as ElasticSearch or Solr.
You can inspire yourself from the following function. It iterates dynamically through all fields of the entity and depending on the type of the field a condition is applied to the query builder:
/**
* Creates the query builder used to get the results of the search query
* performed by the user in the "search" view with a given "keyword".
*
* #param array $entityConfig
* #param string $searchQuery
* #param string|null $sortField
* #param string|null $sortDirection
* #param string|null $dqlFilter
*
* #return DoctrineQueryBuilder
*/
public function createSearchQueryBuilder(array $entityConfig, $searchQuery, $sortField = null, $sortDirection = null, $dqlFilter = null)
{
/* #var EntityManager */
$em = $this->doctrine->getManagerForClass($entityConfig['class']);
/* #var DoctrineQueryBuilder */
$queryBuilder = $em->createQueryBuilder()
->select('entity')
->from($entityConfig['class'], 'entity')
;
$queryParameters = array();
foreach ($entityConfig['search']['fields'] as $name => $metadata) {
$isNumericField = in_array($metadata['dataType'], array('integer', 'number', 'smallint', 'bigint', 'decimal', 'float'));
$isTextField = in_array($metadata['dataType'], array('string', 'text', 'guid'));
if ($isNumericField && is_numeric($searchQuery)) {
$queryBuilder->orWhere(sprintf('entity.%s = :exact_query', $name));
// adding '0' turns the string into a numeric value
$queryParameters['exact_query'] = 0 + $searchQuery;
} elseif ($isTextField) {
$searchQuery = strtolower($searchQuery);
$queryBuilder->orWhere(sprintf('LOWER(entity.%s) LIKE :fuzzy_query', $name));
$queryParameters['fuzzy_query'] = '%'.$searchQuery.'%';
$queryBuilder->orWhere(sprintf('LOWER(entity.%s) IN (:words_query)', $name));
$queryParameters['words_query'] = explode(' ', $searchQuery);
}
}
if (0 !== count($queryParameters)) {
$queryBuilder->setParameters($queryParameters);
}
if (!empty($dqlFilter)) {
$queryBuilder->andWhere($dqlFilter);
}
if (null !== $sortField) {
$queryBuilder->orderBy('entity.'.$sortField, $sortDirection ?: 'DESC');
}
return $queryBuilder;
}
The source code comes from the EasyAdminBundle.

Symfony2 - Accessing tag array which has values is giving an error

I am trying to use a getTags() array which is an array of arrays into another method GetTagWeights($tags) but am getting an error when using it with this line:
$tagWeights[$tag] = (isset($tagWeights[$tag['tag']])) ? $tagWeights[$tag['tag']] + 1 : 1;
I get the following error:
ContextErrorException: Warning: Illegal offset type in /var/www/html/Satori/src/Symfony/AcmeBundle/Entity/TagRepository.php line 34
Question: What am I doing wrong here, I've dumped getTags() and there is data?
My process is getting tags then weighting the tags for popularity. Tag is setup as a ManyToMany/ManyToMany entity with a Blog entity.
getTags and getTagWeight methods (dumping $tags from getTags() returns an array of arrays)
array (size=6)
0 =>
array (size=1)
'tag' => string 'Tag 1' (length=5)
1 =>
array (size=1)
'tag' => string 'Tag 2' (length=5)
2 =>
array (size=1)
'tag' => string 'Tag 3' (length=5)
public function getTags()
{
$tags = $this->createQueryBuilder('t')
->select('t.tag')
->getQuery()
->getResult();
return $tags;
}
public function getTagWeights($tags)
{
$tagWeights = array();
if (empty($tags))
return $tagWeights;
foreach ($tags as $tag)
{
$tagWeights[$tag] = (isset($tagWeights[$tag['tag']])) ? $tagWeights[$tag['tag']] + 1 : 1;
}
// Shuffle the tags
uksort($tagWeights, function() {
return rand() > rand();
});
$max = max($tagWeights);
// Max of 5 weights
$multiplier = ($max > 5) ? 5 / $max : 1;
foreach ($tagWeights as &$tag)
{
$tag = ceil($tag * $multiplier);
}
return $tagWeights;
}
Controller
$em = $this->getDoctrine()->getManager();
$tags = $em->getRepository('AcmeBundle:Tag')
->getTags();
$tagWeights = $em->getRepository('AcmeBundle:Tag')
->getTagWeights($tags);
// var_dump($tagWeights); die();
return array(
'tags' => $tagWeights,
);
Tag entity
class Tag
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="tag", type="string", length=255)
*/
private $tag;
/**
* #ORM\ManyToMany(targetEntity="Blog", mappedBy="tags")
*/
protected $blogs;
public function __construct()
{
$this->blogs = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set tag
*
* #param string $tag
* #return Tag
*/
public function setTag($tag)
{
$this->tag = $tag;
return $this;
}
/**
* Get tag
*
* #return string
*/
public function getTag()
{
return $this->tag;
}
/**
* Add blogs
*
* #param \AcmeBundle\Entity\Blog $blogs
* #return Tag
*/
public function addBlog(\AcmeBundle\Entity\Blog $blogs)
{
$this->blogs[] = $blogs;
return $this;
}
/**
* Remove blogs
*
* #param \AcmeBundle\Entity\Blog $blogs
*/
public function removeBlog(\AcmeBundle\Entity\Blog $blogs)
{
$this->blogs->removeElement($blogs);
}
/**
* Get blogs
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getBlogs()
{
return $this->blogs;
}
}
This is how I am accessing tags in twig:
{% for tag, weight in tags %}
<span class="weight-{{ weight }}">{{ tag.tag }}</span>
{% else %}
<p>There are no tags</p>
{% endfor %}
From the documentation:
Arrays and objects can not be used as keys. Doing so will result in a warning: Illegal offset type.
You are trying to use $tag as key here:
$tagWeights[$tag] = (isset($tagWeights[$tag['tag']])) ? $tagWeights[$tag['tag']] + 1 : 1;
but because $tag is an array, you get an error.
I guess you wanted to do this:
$tagWeights[$tag['tag']] = (isset($tagWeights[$tag['tag']])) ? $tagWeights[$tag['tag']] + 1 : 1;

How to use cakephp export txt file

I'm using cakephp framework.I want to export a database table of a client download in two formats.One is '.csv', the other is a '.txt'.CSV which I have passed 'csvHelper' resolved.Now I want to know how to export '.txt'.Sorry for my poor English, but I think you'll see what I mean, thanks!
Basically you set the header for the filename, like this:
$this->response->type('Content-Type: text/csv');
$this->response->download('myfilename.txt');
Here are some functions I put in my appController, so that any controller can output a CSV file if required:
/**
* Flattens the data that is returned from a find() operation, and puts it into CSV format
* #param $data
* #return string
*/
public function _arrayToCsvFile($data){
$flattenedData = array();
$exportKeyPair = array();
foreach($data as $datumKey => $datumDetails){
$flattenedDatum = Hash::flatten($datumDetails, '.');
$flattenedData[] = $flattenedDatum;
// Find all keys
if($datumKey == 0){
$exportKeys = array_keys($flattenedDatum);
$exportKeyPair = array_combine($exportKeys, $exportKeys);
}
else {
$datumKeys = array_keys($flattenedDatum);
$datumKeyPair = array_combine($datumKeys, $datumKeys);
// Add the datum keys to the exportKeyPair if it's not already there
$exportKeyPair = array_merge($exportKeyPair, $datumKeyPair);
}
}
$exportKeyMap = array();
foreach($exportKeyPair as $exportKey => $exportValue){
$exportKeyMap[$exportKey] = "";
}
$outputCSV = '';
$outputCSV .= $this->_arrayToCsvLine($exportKeyPair);
foreach($flattenedData as $flattenedDatumKey => $flattenedDatumDetails){
// Add any extra keys
$normalisedDatumDetails = array_merge($exportKeyMap, $flattenedDatumDetails);
$outputCSV .= $this->_arrayToCsvLine($normalisedDatumDetails);
}
return $outputCSV;
}
/**
* arrayToCsvLine function - turns an array into a line of CSV data.
*
* #access public
* #param mixed $inputLineArray the input array
* #param string $separator (default: ")
* #param string $quote (default: '"')
* #return string
*/
function _arrayToCsvLine($inputLineArray, $separator = ",", $quote = '"') {
$outputLine = "";
$numOutput = 0;
foreach($inputLineArray as $inputLineKey => $inputLineValue) {
if($numOutput > 0) {
$outputLine .= $separator;
}
$outputLine .= $quote . str_replace(array('"', "\n", "\r"),array('""', "", ""), $inputLineValue) . $quote;
$numOutput++;
}
$outputLine .= "\n";
return $outputLine;
}
/**
* Serves some CSV contents out to the client
* #param $csvContents
* #param array $options
*/
public function _serveCsv($csvContents, $options = array()){
$defaults = array(
'modelClass' => $this->modelClass,
'fileName' => null
);
$settings = array_merge($defaults, $options);
if(empty($settings['fileName'])){
$settings['fileName'] = strtolower( Inflector::pluralize( $settings['modelClass'] ) ) . '_' . date('Y-m-d_H-i') . '.csv';
}
$this->autoRender = false;
$this->response->type('Content-Type: text/csv');
$this->response->download($settings['fileName']);
$this->response->body($csvContents);
}
Now in any controller you can do this (note that you can pass the filename to _serveCsv if required):
/**
* admin_export_csv method
*
* #return void
*/
public function admin_export_csv() {
$this->Order->recursive = 0;
$conditions = array();
$orders = $this->Order->find('all', array('conditions' => $conditions));
$csvContents = $this->_arrayToCsvFile($orders); // Function in AppController
$this->_serveCsv($csvContents, array('filename' => 'export.txt')); // Function in AppController
}
you can use
$this->RequestHandler->respondAs('txt'); to output the correct header.
Please not the header is not set when DEBUG is greater than 2.
And you will still have to build the controller action and set view as ususal.

Resources