I am trying to update a sql database table using the data of an excel file (.csv) in Laravel using the Laravel Excel repository.
My Controller function gives back the content of the excel file, but just in an array of 2 (it should be 604).
Therefore, I think I would have to add 'line ending \t' to my function.But I do not know how.
Here is what I know so far:
The controller
public function uploadExcel()
{
Excel::load(Input::file('import_file'), function ($reader) {
foreach ($reader->toArray() as $value) {
$insert[] = [
'member_title' => $value->member_title,
'member_first_name' => $value->member_first_name,
'member_name_affix' => $value->member_name_affix,
'member_last_name' => $value->member_last_name,
'member_private_address' => $value->member_private_address,
'member_private_zip_code' => $value->member_private_zip_code,
'member_private_location' => $value->member_private_location,
'member_private_phone' => $value->member_private_phone,
'member_private_mobile' => $value->member_private_mobile,
'member_private_fax' => $value->member_private_fax,
'member_private_mail' => $value->member_private_mail,
'member_business_position' => $value->member_business_position,
'member_business_name' => $value->member_business_name,
'member_business_address' => $value->member_business_address,
'member_business_zip_code' => $value->member_business_zip_code,
'member_business_location' => $value->member_business_location,
'member_business_area_code' => $value->member_business_area_code,
'member_business_phone' => $value->member_business_phone,
'member_business_fax' => $value->member_business_fax,
'member_business_mobile' => $value->member_business_mobile,
'member_business_mail' => $value->member_business_mail,
'member_join_date' => $value->member_join_date,
'extra' => $value->extra
];
}
});
if(!empty($insert)) {
die(var_dump($insert)); <-- puts out the array for testing
DB::table('members')->insert($insert);
}
return redirect('index.index');
}
According to the official documentation I would have to add this to my project to recognize the correct line endings:
class UserListImport extends \Maatwebsite\Excel\Files\ExcelFile {
protected $lineEnding = '\t';
}
If my hunch is correct and this snippet from the documentation would solve my problem:
Where do I have to create this file, which contains the code from the documentation? And do I have to change anything else to make that file take affect?
I am really new to Laravel and I would be very thankful for any kind of help!!
UPDATE
Error message
Class App\UserListImport contains 1 abstract method and must therefore
be declared abstract or implement the remaining methods
(Maatwebsite\Excel\Files\ExcelFile::getFile)
app/UserListImport.php
namespace App;
class UserListImport extends \Maatwebsite\Excel\Files\ExcelFile {
protected $lineEnding = '\t';
public function loadExcel() {
Excel::load(Input::file('import_file'), function ($reader) {
foreach ($reader->toArray() as $value) {
$insert[] = [
'member_title' => $value->member_title,
'member_first_name' => $value->member_first_name,
'member_name_affix' => $value->member_name_affix,
'member_last_name' => $value->member_last_name,
'member_private_address' => $value->member_private_address,
'member_private_zip_code' => $value->member_private_zip_code,
'member_private_location' => $value->member_private_location,
'member_private_phone' => $value->member_private_phone,
'member_private_mobile' => $value->member_private_mobile,
'member_private_fax' => $value->member_private_fax,
'member_private_mail' => $value->member_private_mail,
'member_business_position' => $value->member_business_position,
'member_business_name' => $value->member_business_name,
'member_business_address' => $value->member_business_address,
'member_business_zip_code' => $value->member_business_zip_code,
'member_business_location' => $value->member_business_location,
'member_business_area_code' => $value->member_business_area_code,
'member_business_phone' => $value->member_business_phone,
'member_business_fax' => $value->member_business_fax,
'member_business_mobile' => $value->member_business_mobile,
'member_business_mail' => $value->member_business_mail,
'member_join_date' => $value->member_join_date,
'extra' => $value->extra
];
}
});
if(!empty($insert)) {
die(var_dump($insert));
DB::table('members')->insert($insert);
}
return redirect('index.index');
}
}
Controller
use Maatwebsite\Excel\Facades\Excel;
use App\UserListImport;
public function uploadExcel()
{
UserListImport::loadExcel();
}
The docs assumed you created a new custom class extending Excelfile.
class UserListImport extends \Maatwebsite\Excel\Files\ExcelFile
means exacly this.
I usually prefer to add my own application namespaced folders for anything that my application needs.
It this case I would extract anything out from the controller and call my own class to process Input::file('import_file'). That class is the place where to put that attribute to overwrite how the library interacts with the file.
This \t means tabulation(gap between words). For line endings the following combinations of chars are used '\n', '\r\n'. It depends on the operating system. '\n' is for Unix/Linux/Mac OS and '\r\n' is for Windows families.
Related
Sorry not the best title.
I am building an application with many different classes that will be called by a document builder class that takes an Array of what are defined as segments that are then used to reference the segments class to then be built into the document. I provided the Document class and the SegmentConfig file that references which segment points to which file location.
Reference Below First
I could not find a definitive answer on how I could use this to then generate the segment object
could I do it like use $segmentObjectLocation as it would be equal to something like 'Edi\Segments\AmtSegment'. If anyone has some insight.
Other info: the reason I am trying to do it some way like this is becuase this will be used in multiple places though out the application.
$segmentLocation = include('SegmentConfig.php');
class Document{
public function __construct($structure){
$this -> documentStructure = $structure;
foreach($structure as $segment){
buildSegment($segment);
}
}
public function buildSegment($segment){
$segmentObjectLocation = $segmentLocation ->{$segment};
}
}
<?php
namespace Edi;
SegmentConfig.php
return (object) array(
'AMT' => 'Edi\Segments\AmtSegment',
'B4' => 'Edi\Segments\B4Segment',
'BEG' => 'Edi\Segments\BegSegment',
'CTT' => 'Edi\Segments\CttSegment',
'DTM' => 'Edi\Segments\DtmSegment',
'FOB' => 'Edi\Segments\FobSegment',
'GE' => 'Edi\Segments\GeSegment',
'GS' => 'Edi\Segments\GsSegment',
'IEA' => 'Edi\Segments\IeaSegment',
'ISA' => 'Edi\Segments\IsaSegment',
'MSG' => 'Edi\Segments\MsgSegment',
'N1' => 'Edi\Segments\N1Segment',
'N2' => 'Edi\Segments\N2Segment',
'N3' => 'Edi\Segments\N3Segment',
'N4' => 'Edi\Segments\N4Segment',
'N9' => 'Edi\Segments\N9Segment',
'PER' => 'Edi\Segments\PerSegment',
'PID' => 'Edi\Segments\PidSegment',
'PO1' => 'Edi\Segments\Po1Segment',
'Q2' => 'Edi\Segments\Q2Segment',
'R4' => 'Edi\Segments\R4Segment',
'REF' => 'Edi\Segments\RefSegment',
'SAC' => 'Edi\Segments\SacSegment',
'SE' => 'Edi\Segments\SeSegment',
'ST' => 'Edi\Segments\StSegment',
'TC2' => 'Edi\Segments\Tc2Segment',
'TD1' => 'Edi\Segments\Td1Segment',
'TD4' => 'Edi\Segments\Td4Segment',
'TD5' => 'Edi\Segments\Td5Segment',
)
Not that this sounds like a particularly great idea, but:
class Foo {}
$n = 'Foo';
$f = new $n();
var_dump($f);
Output:
object(Foo)#1 (0) {
}
I have a class for configuration on my script and I implement the config. I then want to use the options as an object reference like the following, but not sure how to get it all the way to the final object field and also how to make it recognize sub arrays too
class Configuration {
public $cookies = array(
"cookie_prefix" => "site_",
"site_settings" => array(
"domain" => "somesite.com",
"https_only" => TRUE
),
"another_item" => "and some data too"
);
}
$config = new Configuration();
echo $config->cookies->cookie_prefix;
echo $config->cookies->site_settings->domain;
Right now it works if I do the following
echo $config->cookies['cookie_prefix'];
echo $config->cookies['site_settings']['domain'];
But I want it to be an object all the way down. Can't wrap my brain around this one for some reason?
I know this is easily done - I am just missing the way how...
I just passed the items in the __construct as json and its working the way I wanted now, duh.
public $cookies = array(
"cookie_prefix" => "site_",
"site_settings" => array(
"domain" => "somesite.com",
"https_only" => TRUE
),
"another_item" => "and some data too"
);
public function __construct() {
$this->cookies = json_decode(json_encode($this->cookies));
}
I need to understand the code below, specially how exactly $duplicateChecks and $relationCallbacks work but there is little explanation on the official documentation. Can somebody explain how these work or suggest some other documentation I can look at?
class PlayerCsvBulkLoader extends CsvBulkLoader {
public $columnMap = array(
'Number' => 'PlayerNumber',
'Name' => '->importFirstAndLastName',
'Birthday' => 'Birthday',
'Team' => 'Team.Title',
);
public $duplicateChecks = array(
'Number' => 'PlayerNumber'
);
public $relationCallbacks = array(
'Team.Title' => array(
'relationname' => 'Team',
'callback' => 'getTeamByTitle'
)
);
public static function importFirstAndLastName(&$obj, $val, $record) {
$parts = explode(' ', $val);
if(count($parts) != 2) return false;
$obj->FirstName = $parts[0];
$obj->LastName = $parts[1];
}
public static function getTeamByTitle(&$obj, $val, $record) {
return FootballTeam::get()->filter('Title', $val)->First();
}
}
$duplicateChecks is used by findExistingObject function in the CsvBulkLoader class. It is iterated over to find any object that has a column with the specified value. In that example, it checks the "PlayerNumber" column.
It can also be passed a callback like so:
public $duplicateCheck = array(
'Number' => array(
'callback' => 'checkPlayerNumberFunction'
)
);
The callback specified needs to either exist on an instance of the class specified on the property objectClass or on the CsvBulkLoader itself (which would happen if you extended it). These callbacks are used to do more complex duplicate lookups and return an existing object (if any) found.
$relationCallbacks on the other hand is used by the main processRecord function. The callback works in the same way as the $duplicateCheck callback, it needs to either exist on an instance of the class specified on the proeprty objectClass or on the CsvBulkLoader. These callbacks can return an object that will be related back to a specific object record (new or existing) as a has_one.
There is a little more to it than that though the best way to learn is by a bit of experimentation and jumping through the code of the class itself. I have linked to the various functions etc in my answer.
Not quite sure of the best title but I will explain what I am asking as best I can. Assume I have the following file:
MyCustomClass.php
<?php
namespace MyNamespace;
use FooNamespace\FooClass;
use BarNamespace\BarClass as Bar;
use BazNamespace\BazClass as BazSpecial;
class MyCustomClass {
protected $someDependencies = [];
public function __construct(FooClass $foo, Bar $bar) {
$someDependencies[] = $foo;
$someDependencies[] = $bar;
}
}
Now if I were to use reflection, I could get the fully qualified class names from the type hints in the construct.
However, I would recieve FooNamespace\FooClass and BarNamespace\BarClass. Not, FooNamespace\FooClass and BarNamespace\Bar. I would also get no reference to BazNamespace\BazClass.
Basically, my question is: How can I get the fully qualified names from MyCustomClass.php while only knowing FooClass, Bar, and, BazSpecial?
I do not want to use a file parser as this will eat performance. I want to be able to do something like:
$class = new ReflectionClass('MyCustomClass');
...
$class->getUsedClass('FooClass'); // FooNamespace\FooClass
$class->getUsedClass('Bar'); // BarNamespace\BarClass
$class->getUsedClass('BazSpecial'); // BazNamespace\BazClass
How would I go about doing this?
Seeing as no one has answered, I assume there is not an easy way to achieve this. I have therefore created my own class called ExtendedReflectionClass which achieves what I need.
I have created a gist with the class file and a readme, which is at the bottom so get scrolling!.
ExtendedReflectionClass
Usage example:
require 'ExtendedReflectionClass.php';
require 'MyCustomClass.php';
$class = new ExtendedReflectionClass('MyNamespace\Test\MyCustomClass');
$class->getUseStatements();
// [
// [
// 'class' => 'FooNamespace\FooClass',
// 'as' => 'FooClass'
// ],
// [
// 'class' => 'BarNamespace\BarClass',
// 'as' => 'Bar'
// ],
// [
// 'class' => 'BazNamespace\BazClass',
// 'as' => 'BazSpecial'
// ]
// ]
$class->hasUseStatement('FooClass'); // true
$class->hasUseStatement('BarNamespace\BarClass'); // true
$class->hasUseStatement('BazSpecial'); // true
$class->hasUseStatement('SomeNamespace\SomeClass'); // false
I use the TokenFinderTool for that.
Basically, it uses tokens to extract the use statements.
As far as I know, \Reflection objects in php unfortunately don't have such a method yet.
The code below extracts the use import statements from a file, using the TokenFinder tool.
$tokens = token_get_all(file_get_contents("/path/to/MyCompany/MyClass.php"));
a(TokenFinderTool::getUseDependencies($tokens));
Will output:
array (size=9)
0 => string 'Bat\CaseTool' (length=12)
1 => string 'Bat\FileSystemTool' (length=18)
2 => string 'Bat\StringTool' (length=14)
3 => string 'Bat\ValidationTool' (length=18)
4 => string 'CopyDir\AuthorCopyDirUtil' (length=25)
5 => string 'PhpBeast\AuthorTestAggregator' (length=29)
6 => string 'PhpBeast\PrettyTestInterpreter' (length=30)
7 => string 'PhpBeast\Tool\ComparisonErrorTableTool' (length=38)
8 => string 'Tiphaine\TiphaineTool' (length=21)
Note: if you have a class name only, you can use this snippet instead:
$o = new \ReflectionClass($className);
$tokens = token_get_all(file_get_contents($$o->getFileName()));
$useStatements = TokenFinderTool::getUseDependencies($tokens);
Use BetterReflection (composer req roave/better-reflection)
$classInfo = (new BetterReflection())
->reflector()
->reflectClass($class);
$uses = [];
foreach ($classInfo->getDeclaringNamespaceAst()->stmts as $stmt) {
foreach ($stmt->uses??[] as $use) {
$uses[] = join('\\', $use->name->parts);
}
}
print_r($uses);
Using the latest CakePHP build 1.3.6.
I'm writing a custom datasource for a external REST API. I've got all the read functionality working beautifully. I'm struggling with the Model::save & Model::create.
According to the documentation, the below methods must be implemented (see below and notice it does not mention calculate). These are all implemented. However, I was getting an "Fatal error: Call to undefined method ApiSource::calculate()". So I implemented the ApiSource::calculate() method.
describe($model) listSources() At
least one of:
create($model, $fields = array(), $values = array())
read($model, $queryData = array())
update($model, $fields = array(), $values = array())
delete($model, $id
= null)
public function calculate(&$model, $func, $params = array())
{
pr($model->data); // POST data
pr($func); // count
pr($params); // empty
return '__'.$func; // returning __count;
}
If make a call from my model
$this->save($this->data)
It is calling calculate, but none of the other implemented methods. I would expect it to either call ApiSource::create() or ApiSource::update()
Any thoughts or suggustions?
Leo, you tipped me in the right direction. The answer was in the model that was using the custom datasource. That model MUST define your _schema.
class User extends AppModel
{
public $name = 'User';
public $useDbConfig = 'cvs';
public $useTable = false;
public $_schema = array(
'firstName' => array(
'type' => 'string',
'length' => 30
),
'lastName' => array(
'type' => 'string',
'length' => 30
),
'email' => array(
'type' => 'string',
'length' => 50
),
'password' => array(
'type' => 'string',
'length' => 20
)
);
...
}
I'm guessing that if you implement a describe() method in the custom datasource that will solve the problem too. In this case it needed to be predefined to authorize the saves and/or creation.
From the API: http://api13.cakephp.org/class/dbo-source#method-DboSourcecalculate
"Returns an SQL calculation, i.e. COUNT() or MAX()"
A quick search in ~/cake finds 20 matches in 8 files. One of those is the definition in dbo_source.php
The other seven are:
dbo_source.test.php
code_coverage_manager.test.php
code_coverage_manager.php
dbo_db2.php
model.php
tree.php
containable.php
Without delving too deeply into this, I suspect your problem lies in Model::save
You'll probably have to define a calculate method to suit the structure of your custom datasource because Cake won't know how to do that.